Line data Source code
1 : /*
2 : * System Security Services Daemon. NSS client interface
3 : *
4 : * Copyright (C) Simo Sorce 2007
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of the
9 : * License, or (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 Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public License
17 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /* PASSWD database NSS interface */
21 :
22 : #include <nss.h>
23 : #include <errno.h>
24 : #include <sys/types.h>
25 : #include <unistd.h>
26 : #include <stdlib.h>
27 : #include <stdint.h>
28 : #include <string.h>
29 : #include "sss_cli.h"
30 : #include "nss_mc.h"
31 :
32 : static struct sss_nss_getpwent_data {
33 : size_t len;
34 : size_t ptr;
35 : uint8_t *data;
36 : } sss_nss_getpwent_data;
37 :
38 0 : static void sss_nss_getpwent_data_clean(void) {
39 :
40 0 : if (sss_nss_getpwent_data.data != NULL) {
41 0 : free(sss_nss_getpwent_data.data);
42 0 : sss_nss_getpwent_data.data = NULL;
43 : }
44 0 : sss_nss_getpwent_data.len = 0;
45 0 : sss_nss_getpwent_data.ptr = 0;
46 0 : }
47 :
48 : /* GETPWNAM Request:
49 : *
50 : * 0-X: string with name
51 : *
52 : * GERTPWUID Request:
53 : *
54 : * 0-3: 32bit number with uid
55 : *
56 : * Replies:
57 : *
58 : * 0-3: 32bit unsigned number of results
59 : * 4-7: 32bit unsigned (reserved/padding)
60 : * For each result:
61 : * 0-3: 32bit number uid
62 : * 4-7: 32bit number gid
63 : * 8-X: sequence of 5, 0 terminated, strings (name, passwd, gecos, dir, shell)
64 : */
65 :
66 : struct sss_nss_pw_rep {
67 : struct passwd *result;
68 : char *buffer;
69 : size_t buflen;
70 : };
71 :
72 0 : static int sss_nss_getpw_readrep(struct sss_nss_pw_rep *pr,
73 : uint8_t *buf, size_t *len)
74 : {
75 : errno_t ret;
76 : size_t i, slen, dlen;
77 : char *sbuf;
78 : uint32_t c;
79 :
80 0 : if (*len < 13) { /* not enough space for data, bad packet */
81 0 : return EBADMSG;
82 : }
83 :
84 0 : SAFEALIGN_COPY_UINT32(&c, buf, NULL);
85 0 : pr->result->pw_uid = c;
86 0 : SAFEALIGN_COPY_UINT32(&c, buf+sizeof(uint32_t), NULL);
87 0 : pr->result->pw_gid = c;
88 :
89 0 : sbuf = (char *)&buf[8];
90 0 : slen = *len - 8;
91 0 : dlen = pr->buflen;
92 :
93 0 : i = 0;
94 0 : pr->result->pw_name = &(pr->buffer[i]);
95 :
96 0 : ret = sss_readrep_copy_string(sbuf, &i,
97 : &slen, &dlen,
98 0 : &pr->result->pw_name,
99 : NULL);
100 0 : if (ret != EOK) return ret;
101 :
102 0 : pr->result->pw_passwd = &(pr->buffer[i]);
103 0 : ret = sss_readrep_copy_string(sbuf, &i,
104 : &slen, &dlen,
105 0 : &pr->result->pw_passwd,
106 : NULL);
107 0 : if (ret != EOK) return ret;
108 :
109 0 : pr->result->pw_gecos = &(pr->buffer[i]);
110 0 : ret = sss_readrep_copy_string(sbuf, &i,
111 : &slen, &dlen,
112 0 : &pr->result->pw_gecos,
113 : NULL);
114 0 : if (ret != EOK) return ret;
115 :
116 :
117 0 : pr->result->pw_dir = &(pr->buffer[i]);
118 0 : ret = sss_readrep_copy_string(sbuf, &i,
119 : &slen, &dlen,
120 0 : &pr->result->pw_dir,
121 : NULL);
122 0 : if (ret != EOK) return ret;
123 :
124 0 : pr->result->pw_shell = &(pr->buffer[i]);
125 0 : ret = sss_readrep_copy_string(sbuf, &i,
126 : &slen, &dlen,
127 0 : &pr->result->pw_shell,
128 : NULL);
129 0 : if (ret != EOK) return ret;
130 0 : *len = slen - i;
131 :
132 0 : return 0;
133 : }
134 :
135 38 : enum nss_status _nss_sss_getpwnam_r(const char *name, struct passwd *result,
136 : char *buffer, size_t buflen, int *errnop)
137 : {
138 : struct sss_cli_req_data rd;
139 : struct sss_nss_pw_rep pwrep;
140 : uint8_t *repbuf;
141 : size_t replen, len, name_len;
142 : uint32_t num_results;
143 : enum nss_status nret;
144 : int ret;
145 :
146 : /* Caught once glibc passing in buffer == 0x0 */
147 38 : if (!buffer || !buflen) {
148 0 : *errnop = ERANGE;
149 0 : return NSS_STATUS_TRYAGAIN;
150 : }
151 :
152 38 : ret = sss_strnlen(name, SSS_NAME_MAX, &name_len);
153 38 : if (ret != 0) {
154 0 : *errnop = EINVAL;
155 0 : return NSS_STATUS_NOTFOUND;
156 : }
157 :
158 38 : ret = sss_nss_mc_getpwnam(name, name_len, result, buffer, buflen);
159 38 : switch (ret) {
160 : case 0:
161 0 : *errnop = 0;
162 0 : return NSS_STATUS_SUCCESS;
163 : case ERANGE:
164 0 : *errnop = ERANGE;
165 0 : return NSS_STATUS_TRYAGAIN;
166 : case ENOENT:
167 : /* fall through, we need to actively ask the parent
168 : * if no entry is found */
169 38 : break;
170 : default:
171 : /* if using the mmaped cache failed,
172 : * fall back to socket based comms */
173 0 : break;
174 : }
175 :
176 38 : rd.len = name_len + 1;
177 38 : rd.data = name;
178 :
179 38 : sss_nss_lock();
180 :
181 : /* previous thread might already initialize entry in mmap cache */
182 38 : ret = sss_nss_mc_getpwnam(name, name_len, result, buffer, buflen);
183 38 : switch (ret) {
184 : case 0:
185 0 : *errnop = 0;
186 0 : nret = NSS_STATUS_SUCCESS;
187 0 : goto out;
188 : case ERANGE:
189 0 : *errnop = ERANGE;
190 0 : nret = NSS_STATUS_TRYAGAIN;
191 0 : goto out;
192 : case ENOENT:
193 : /* fall through, we need to actively ask the parent
194 : * if no entry is found */
195 38 : break;
196 : default:
197 : /* if using the mmaped cache failed,
198 : * fall back to socket based comms */
199 0 : break;
200 : }
201 :
202 38 : nret = sss_nss_make_request(SSS_NSS_GETPWNAM, &rd,
203 : &repbuf, &replen, errnop);
204 38 : if (nret != NSS_STATUS_SUCCESS) {
205 38 : goto out;
206 : }
207 :
208 0 : pwrep.result = result;
209 0 : pwrep.buffer = buffer;
210 0 : pwrep.buflen = buflen;
211 :
212 : /* Get number of results from repbuf. */
213 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
214 :
215 : /* no results if not found */
216 0 : if (num_results == 0) {
217 0 : free(repbuf);
218 0 : nret = NSS_STATUS_NOTFOUND;
219 0 : goto out;
220 : }
221 :
222 : /* only 1 result is accepted for this function */
223 0 : if (num_results != 1) {
224 0 : *errnop = EBADMSG;
225 0 : free(repbuf);
226 0 : nret = NSS_STATUS_TRYAGAIN;
227 0 : goto out;
228 : }
229 :
230 0 : len = replen - 8;
231 0 : ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len);
232 0 : free(repbuf);
233 0 : if (ret) {
234 0 : *errnop = ret;
235 0 : nret = NSS_STATUS_TRYAGAIN;
236 0 : goto out;
237 : }
238 :
239 0 : nret = NSS_STATUS_SUCCESS;
240 :
241 : out:
242 38 : sss_nss_unlock();
243 38 : return nret;
244 : }
245 :
246 1 : enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result,
247 : char *buffer, size_t buflen, int *errnop)
248 : {
249 : struct sss_cli_req_data rd;
250 : struct sss_nss_pw_rep pwrep;
251 : uint8_t *repbuf;
252 : size_t replen, len;
253 : uint32_t num_results;
254 : enum nss_status nret;
255 : uint32_t user_uid;
256 : int ret;
257 :
258 : /* Caught once glibc passing in buffer == 0x0 */
259 1 : if (!buffer || !buflen) return ERANGE;
260 :
261 1 : ret = sss_nss_mc_getpwuid(uid, result, buffer, buflen);
262 1 : switch (ret) {
263 : case 0:
264 0 : *errnop = 0;
265 0 : return NSS_STATUS_SUCCESS;
266 : case ERANGE:
267 0 : *errnop = ERANGE;
268 0 : return NSS_STATUS_TRYAGAIN;
269 : case ENOENT:
270 : /* fall through, we need to actively ask the parent
271 : * if no entry is found */
272 1 : break;
273 : default:
274 : /* if using the mmaped cache failed,
275 : * fall back to socket based comms */
276 0 : break;
277 : }
278 :
279 1 : user_uid = uid;
280 1 : rd.len = sizeof(uint32_t);
281 1 : rd.data = &user_uid;
282 :
283 1 : sss_nss_lock();
284 :
285 : /* previous thread might already initialize entry in mmap cache */
286 1 : ret = sss_nss_mc_getpwuid(uid, result, buffer, buflen);
287 1 : switch (ret) {
288 : case 0:
289 0 : *errnop = 0;
290 0 : nret = NSS_STATUS_SUCCESS;
291 0 : goto out;
292 : case ERANGE:
293 0 : *errnop = ERANGE;
294 0 : nret = NSS_STATUS_TRYAGAIN;
295 0 : goto out;
296 : case ENOENT:
297 : /* fall through, we need to actively ask the parent
298 : * if no entry is found */
299 1 : break;
300 : default:
301 : /* if using the mmaped cache failed,
302 : * fall back to socket based comms */
303 0 : break;
304 : }
305 :
306 1 : nret = sss_nss_make_request(SSS_NSS_GETPWUID, &rd,
307 : &repbuf, &replen, errnop);
308 1 : if (nret != NSS_STATUS_SUCCESS) {
309 1 : goto out;
310 : }
311 :
312 0 : pwrep.result = result;
313 0 : pwrep.buffer = buffer;
314 0 : pwrep.buflen = buflen;
315 :
316 : /* Get number of results from repbuf. */
317 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
318 :
319 : /* no results if not found */
320 0 : if (num_results == 0) {
321 0 : free(repbuf);
322 0 : nret = NSS_STATUS_NOTFOUND;
323 0 : goto out;
324 : }
325 :
326 : /* only 1 result is accepted for this function */
327 0 : if (num_results != 1) {
328 0 : *errnop = EBADMSG;
329 0 : free(repbuf);
330 0 : nret = NSS_STATUS_TRYAGAIN;
331 0 : goto out;
332 : }
333 :
334 0 : len = replen - 8;
335 0 : ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len);
336 0 : free(repbuf);
337 0 : if (ret) {
338 0 : *errnop = ret;
339 0 : nret = NSS_STATUS_TRYAGAIN;
340 0 : goto out;
341 : }
342 :
343 0 : nret = NSS_STATUS_SUCCESS;
344 :
345 : out:
346 1 : sss_nss_unlock();
347 1 : return nret;
348 : }
349 :
350 0 : enum nss_status _nss_sss_setpwent(void)
351 : {
352 : enum nss_status nret;
353 : int errnop;
354 :
355 0 : sss_nss_lock();
356 :
357 : /* make sure we do not have leftovers, and release memory */
358 0 : sss_nss_getpwent_data_clean();
359 :
360 0 : nret = sss_nss_make_request(SSS_NSS_SETPWENT,
361 : NULL, NULL, NULL, &errnop);
362 0 : if (nret != NSS_STATUS_SUCCESS) {
363 0 : errno = errnop;
364 : }
365 :
366 0 : sss_nss_unlock();
367 0 : return nret;
368 : }
369 :
370 0 : static enum nss_status internal_getpwent_r(struct passwd *result,
371 : char *buffer, size_t buflen,
372 : int *errnop)
373 : {
374 : struct sss_cli_req_data rd;
375 : struct sss_nss_pw_rep pwrep;
376 : uint8_t *repbuf;
377 : size_t replen;
378 : uint32_t num_results;
379 : enum nss_status nret;
380 : uint32_t num_entries;
381 : int ret;
382 :
383 : /* Caught once glibc passing in buffer == 0x0 */
384 0 : if (!buffer || !buflen) return ERANGE;
385 :
386 : /* if there are leftovers return the next one */
387 0 : if (sss_nss_getpwent_data.data != NULL &&
388 0 : sss_nss_getpwent_data.ptr < sss_nss_getpwent_data.len) {
389 :
390 0 : repbuf = sss_nss_getpwent_data.data + sss_nss_getpwent_data.ptr;
391 0 : replen = sss_nss_getpwent_data.len - sss_nss_getpwent_data.ptr;
392 :
393 0 : pwrep.result = result;
394 0 : pwrep.buffer = buffer;
395 0 : pwrep.buflen = buflen;
396 :
397 0 : ret = sss_nss_getpw_readrep(&pwrep, repbuf, &replen);
398 0 : if (ret) {
399 0 : *errnop = ret;
400 0 : return NSS_STATUS_TRYAGAIN;
401 : }
402 :
403 : /* advance buffer pointer */
404 0 : sss_nss_getpwent_data.ptr = sss_nss_getpwent_data.len - replen;
405 :
406 0 : return NSS_STATUS_SUCCESS;
407 : }
408 :
409 : /* release memory if any */
410 0 : sss_nss_getpwent_data_clean();
411 :
412 : /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
413 0 : num_entries = SSS_NSS_MAX_ENTRIES;
414 0 : rd.len = sizeof(uint32_t);
415 0 : rd.data = &num_entries;
416 :
417 0 : nret = sss_nss_make_request(SSS_NSS_GETPWENT, &rd,
418 : &repbuf, &replen, errnop);
419 0 : if (nret != NSS_STATUS_SUCCESS) {
420 0 : return nret;
421 : }
422 :
423 : /* Get number of results from repbuf. */
424 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
425 :
426 : /* no results if not found */
427 0 : if ((num_results == 0) || (replen - 8 == 0)) {
428 0 : free(repbuf);
429 0 : return NSS_STATUS_NOTFOUND;
430 : }
431 :
432 0 : sss_nss_getpwent_data.data = repbuf;
433 0 : sss_nss_getpwent_data.len = replen;
434 0 : sss_nss_getpwent_data.ptr = 8; /* skip metadata fields */
435 :
436 : /* call again ourselves, this will return the first result */
437 0 : return internal_getpwent_r(result, buffer, buflen, errnop);
438 : }
439 :
440 0 : enum nss_status _nss_sss_getpwent_r(struct passwd *result,
441 : char *buffer, size_t buflen,
442 : int *errnop)
443 : {
444 : enum nss_status nret;
445 :
446 0 : sss_nss_lock();
447 0 : nret = internal_getpwent_r(result, buffer, buflen, errnop);
448 0 : sss_nss_unlock();
449 :
450 0 : return nret;
451 : }
452 :
453 0 : enum nss_status _nss_sss_endpwent(void)
454 : {
455 : enum nss_status nret;
456 : int errnop;
457 :
458 0 : sss_nss_lock();
459 :
460 : /* make sure we do not have leftovers, and release memory */
461 0 : sss_nss_getpwent_data_clean();
462 :
463 0 : nret = sss_nss_make_request(SSS_NSS_ENDPWENT,
464 : NULL, NULL, NULL, &errnop);
465 0 : if (nret != NSS_STATUS_SUCCESS) {
466 0 : errno = errnop;
467 : }
468 :
469 0 : sss_nss_unlock();
470 0 : return nret;
471 : }
|