]> ruderich.org/simon Gitweb - nsscash/nsscash.git/blob - nss/pw.c
1dd9d3b5c05279195af7ccbc2497035305735565
[nsscash/nsscash.git] / nss / pw.c
1 /*
2  * Handle passwd entries via struct passwd
3  *
4  * Copyright (C) 2019  Simon Ruderich
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <errno.h>
21 #include <stddef.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <pthread.h>
27
28 #include "cash.h"
29 #include "cash_nss.h"
30 #include "file.h"
31 #include "search.h"
32
33
34 // NOTE: This file is very similar to gr.c, keep in sync!
35
36 struct passwd_entry {
37     uint64_t uid;
38     uint64_t gid;
39
40     //       off_name = 0
41     uint16_t off_passwd;
42     uint16_t off_gecos;
43     uint16_t off_dir;
44     uint16_t off_shell;
45
46     uint16_t data_size;
47     /*
48      * Data contains all strings (name, passwd, gecos, dir, shell)
49      * concatenated, with their trailing NUL. The off_* variables point to
50      * beginning of each string.
51      */
52     char data[];
53 } __attribute__((packed));
54
55 static bool entry_to_passwd(const struct passwd_entry *e, struct passwd *p, char *tmp, size_t space) {
56     if (space < e->data_size) {
57         return false;
58     }
59
60     memcpy(tmp, e->data, e->data_size);
61     p->pw_uid = (uid_t)e->uid;
62     p->pw_gid = (gid_t)e->gid;
63     p->pw_name = tmp + 0;
64     p->pw_passwd = tmp + e->off_passwd;
65     p->pw_gecos = tmp + e->off_gecos;
66     p->pw_dir = tmp + e->off_dir;
67     p->pw_shell = tmp + e->off_shell;
68
69     return true;
70 }
71
72
73 static struct file static_file = {
74     .fd = -1,
75 };
76 static pthread_mutex_t static_file_lock = PTHREAD_MUTEX_INITIALIZER;
77
78 enum nss_status _nss_cash_setpwent(int x) {
79     (void)x;
80
81     pthread_mutex_lock(&static_file_lock);
82     // Unmap is necessary to detect changes when the file was replaced on
83     // disk
84     unmap_file(&static_file);
85     // getpwent_r will open the file if necessary when called
86     pthread_mutex_unlock(&static_file_lock);
87
88     return NSS_STATUS_SUCCESS;
89 }
90
91 enum nss_status _nss_cash_endpwent(void) {
92     pthread_mutex_lock(&static_file_lock);
93     unmap_file(&static_file);
94     pthread_mutex_unlock(&static_file_lock);
95
96     return NSS_STATUS_SUCCESS;
97 }
98
99 static enum nss_status internal_getpwent_r(struct passwd *result, char *buffer, size_t buflen) {
100     // First call to getpwent_r, load file from disk
101     if (static_file.header == NULL) {
102         if (!map_file(NSSCASH_PASSWD_FILE, &static_file)) {
103             return NSS_STATUS_UNAVAIL;
104         }
105     }
106
107     const struct header *h = static_file.header;
108     // End of "file", stop
109     if (static_file.next_index >= h->count) {
110         errno = ENOENT;
111         return NSS_STATUS_NOTFOUND;
112     }
113
114     uint64_t *off_orig = (uint64_t *)(h->data + h->off_orig_index);
115     const char *e = h->data + h->off_data + off_orig[static_file.next_index];
116     if (!entry_to_passwd((struct passwd_entry *)e, result, buffer, buflen)) {
117         errno = ERANGE;
118         return NSS_STATUS_TRYAGAIN;
119     }
120     static_file.next_index++;
121
122     return NSS_STATUS_SUCCESS;
123 }
124 enum nss_status _nss_cash_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
125     pthread_mutex_lock(&static_file_lock);
126     enum nss_status s = internal_getpwent_r(result, buffer, buflen);
127     pthread_mutex_unlock(&static_file_lock);
128     if (s != NSS_STATUS_SUCCESS) {
129         *errnop = errno;
130     }
131     return s;
132 }
133
134
135 static enum nss_status internal_getpw(struct search_key *key, struct passwd *result, char *buffer, size_t buflen, int *errnop) {
136     struct file f;
137     if (!map_file(NSSCASH_PASSWD_FILE, &f)) {
138         *errnop = errno;
139         return NSS_STATUS_UNAVAIL;
140     }
141     const struct header *h = f.header;
142
143     key->data = h->data + h->off_data;
144     uint64_t off_index = (key->id != NULL)
145                        ? h->off_id_index
146                        : h->off_name_index;
147     uint64_t *off = search(key, h->data + off_index, h->count);
148     if (off == NULL) {
149         unmap_file(&f);
150         errno = ENOENT;
151         *errnop = errno;
152         return NSS_STATUS_NOTFOUND;
153     }
154
155     const char *e = h->data + h->off_data + *off;
156     if (!entry_to_passwd((struct passwd_entry *)e, result, buffer, buflen)) {
157         unmap_file(&f);
158         errno = ERANGE;
159         *errnop = errno;
160         return NSS_STATUS_TRYAGAIN;
161     }
162
163     unmap_file(&f);
164     return NSS_STATUS_SUCCESS;
165 }
166
167 enum nss_status _nss_cash_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop) {
168     uint64_t id = (uint64_t)uid;
169     struct search_key key = {
170         .id = &id,
171         .offset = offsetof(struct passwd_entry, uid),
172     };
173     return internal_getpw(&key, result, buffer, buflen, errnop);
174 }
175
176 enum nss_status _nss_cash_getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop) {
177     struct search_key key = {
178         .name = name,
179         .offset = sizeof(struct passwd_entry), // name is first value in data[]
180     };
181     return internal_getpw(&key, result, buffer, buflen, errnop);
182 }