1 | /* Copyright (C) 1996-2017 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <assert.h> |
20 | #include <ctype.h> |
21 | #include <errno.h> |
22 | #include <nss.h> |
23 | #include <pwd.h> |
24 | #include <string.h> |
25 | #include <libc-lock.h> |
26 | #include <rpcsvc/yp.h> |
27 | #include <rpcsvc/ypclnt.h> |
28 | |
29 | #include "nss-nis.h" |
30 | #include <libnsl.h> |
31 | |
32 | /* Get the declaration of the parser function. */ |
33 | #define ENTNAME pwent |
34 | #define STRUCTURE passwd |
35 | #define EXTERN_PARSER |
36 | #include <nss/nss_files/files-parse.c> |
37 | |
38 | /* Protect global state against multiple changers */ |
39 | __libc_lock_define_initialized (static, lock) |
40 | |
41 | static bool new_start = true; |
42 | static char *oldkey; |
43 | static int oldkeylen; |
44 | static intern_t intern; |
45 | |
46 | |
47 | int |
48 | _nis_saveit (int instatus, char *inkey, int inkeylen, char *inval, |
49 | int invallen, char *indata) |
50 | { |
51 | intern_t *intern = (intern_t *) indata; |
52 | |
53 | if (instatus != YP_TRUE) |
54 | return 1; |
55 | |
56 | if (inkey && inkeylen > 0 && inval && invallen > 0) |
57 | { |
58 | struct response_t *bucket = intern->next; |
59 | |
60 | if (__glibc_unlikely (bucket == NULL)) |
61 | { |
62 | #define MINSIZE 4096 - 4 * sizeof (void *) |
63 | const size_t minsize = MAX (MINSIZE, 2 * (invallen + 1)); |
64 | bucket = malloc (sizeof (struct response_t) + minsize); |
65 | if (bucket == NULL) |
66 | /* We have no error code for out of memory. */ |
67 | return 1; |
68 | |
69 | bucket->next = NULL; |
70 | bucket->size = minsize; |
71 | intern->start = intern->next = bucket; |
72 | intern->offset = 0; |
73 | } |
74 | else if (__builtin_expect (invallen + 1 > bucket->size - intern->offset, |
75 | 0)) |
76 | { |
77 | /* We need a new (larger) buffer. */ |
78 | const size_t newsize = 2 * MAX (bucket->size, invallen + 1); |
79 | struct response_t *newp = malloc (sizeof (struct response_t) |
80 | + newsize); |
81 | if (newp == NULL) |
82 | /* We have no error code for out of memory. */ |
83 | return 1; |
84 | |
85 | /* Mark the old bucket as full. */ |
86 | bucket->size = intern->offset; |
87 | |
88 | newp->next = NULL; |
89 | newp->size = newsize; |
90 | bucket = intern->next = bucket->next = newp; |
91 | intern->offset = 0; |
92 | } |
93 | |
94 | char *p = mempcpy (&bucket->mem[intern->offset], inval, invallen); |
95 | if (__glibc_unlikely (p[-1] != '\0')) |
96 | { |
97 | *p = '\0'; |
98 | ++invallen; |
99 | } |
100 | intern->offset += invallen; |
101 | } |
102 | |
103 | return 0; |
104 | } |
105 | |
106 | |
107 | static void |
108 | internal_nis_endpwent (void) |
109 | { |
110 | new_start = true; |
111 | free (oldkey); |
112 | oldkey = NULL; |
113 | oldkeylen = 0; |
114 | |
115 | struct response_t *curr = intern.start; |
116 | |
117 | while (curr != NULL) |
118 | { |
119 | struct response_t *last = curr; |
120 | curr = curr->next; |
121 | free (last); |
122 | } |
123 | |
124 | intern.next = intern.start = NULL; |
125 | } |
126 | |
127 | |
128 | enum nss_status |
129 | _nss_nis_endpwent (void) |
130 | { |
131 | __libc_lock_lock (lock); |
132 | |
133 | internal_nis_endpwent (); |
134 | |
135 | __libc_lock_unlock (lock); |
136 | |
137 | return NSS_STATUS_SUCCESS; |
138 | } |
139 | |
140 | |
141 | enum nss_status |
142 | internal_nis_setpwent (void) |
143 | { |
144 | /* We have to read all the data now. */ |
145 | char *domain; |
146 | if (__glibc_unlikely (yp_get_default_domain (&domain))) |
147 | return NSS_STATUS_UNAVAIL; |
148 | |
149 | struct ypall_callback ypcb; |
150 | |
151 | ypcb.foreach = _nis_saveit; |
152 | ypcb.data = (char *) &intern; |
153 | enum nss_status status = yperr2nss (yp_all (domain, "passwd.byname" , &ypcb)); |
154 | |
155 | |
156 | /* Mark the last buffer as full. */ |
157 | if (intern.next != NULL) |
158 | intern.next->size = intern.offset; |
159 | |
160 | intern.next = intern.start; |
161 | intern.offset = 0; |
162 | |
163 | return status; |
164 | } |
165 | |
166 | |
167 | enum nss_status |
168 | _nss_nis_setpwent (int stayopen) |
169 | { |
170 | enum nss_status result = NSS_STATUS_SUCCESS; |
171 | |
172 | __libc_lock_lock (lock); |
173 | |
174 | internal_nis_endpwent (); |
175 | |
176 | if (_nsl_default_nss () & NSS_FLAG_SETENT_BATCH_READ) |
177 | result = internal_nis_setpwent (); |
178 | |
179 | __libc_lock_unlock (lock); |
180 | |
181 | return result; |
182 | } |
183 | |
184 | |
185 | static enum nss_status |
186 | internal_nis_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen, |
187 | int *errnop) |
188 | { |
189 | /* If we read the entire database at setpwent time we just iterate |
190 | over the data we have in memory. */ |
191 | bool batch_read = intern.start != NULL; |
192 | |
193 | char *domain = NULL; |
194 | if (!batch_read && __builtin_expect (yp_get_default_domain (&domain), 0)) |
195 | return NSS_STATUS_UNAVAIL; |
196 | |
197 | /* Get the next entry until we found a correct one. */ |
198 | int parse_res; |
199 | do |
200 | { |
201 | char *result; |
202 | char *outkey; |
203 | int len; |
204 | int keylen; |
205 | |
206 | if (batch_read) |
207 | { |
208 | struct response_t *bucket; |
209 | |
210 | handle_batch_read: |
211 | bucket = intern.next; |
212 | |
213 | if (__glibc_unlikely (intern.offset >= bucket->size)) |
214 | { |
215 | if (bucket->next == NULL) |
216 | return NSS_STATUS_NOTFOUND; |
217 | |
218 | /* We look at all the content in the current bucket. Go on |
219 | to the next. */ |
220 | bucket = intern.next = bucket->next; |
221 | intern.offset = 0; |
222 | } |
223 | |
224 | for (result = &bucket->mem[intern.offset]; isspace (*result); |
225 | ++result) |
226 | ++intern.offset; |
227 | |
228 | len = strlen (result); |
229 | } |
230 | else |
231 | { |
232 | int yperr; |
233 | |
234 | if (new_start) |
235 | { |
236 | /* Maybe we should read the database in one piece. */ |
237 | if ((_nsl_default_nss () & NSS_FLAG_SETENT_BATCH_READ) |
238 | && internal_nis_setpwent () == NSS_STATUS_SUCCESS |
239 | && intern.start != NULL) |
240 | { |
241 | batch_read = true; |
242 | goto handle_batch_read; |
243 | } |
244 | |
245 | yperr = yp_first (domain, "passwd.byname" , &outkey, &keylen, |
246 | &result, &len); |
247 | } |
248 | else |
249 | yperr = yp_next (domain, "passwd.byname" , oldkey, oldkeylen, |
250 | &outkey, &keylen, &result, &len); |
251 | |
252 | if (__glibc_unlikely (yperr != YPERR_SUCCESS)) |
253 | { |
254 | enum nss_status retval = yperr2nss (yperr); |
255 | |
256 | if (retval == NSS_STATUS_TRYAGAIN) |
257 | *errnop = errno; |
258 | return retval; |
259 | } |
260 | } |
261 | |
262 | /* Check for adjunct style secret passwords. They can be |
263 | recognized by a password starting with "##". We do not use |
264 | it if the passwd.adjunct.byname table is supposed to be used |
265 | as a shadow.byname replacement. */ |
266 | char *p = strchr (result, ':'); |
267 | size_t namelen; |
268 | char *result2; |
269 | int len2; |
270 | if ((_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW) == 0 |
271 | && p != NULL /* This better should be true in all cases. */ |
272 | && p[1] == '#' && p[2] == '#' |
273 | && (namelen = p - result, |
274 | yp_match (domain, "passwd.adjunct.byname" , result, namelen, |
275 | &result2, &len2)) == YPERR_SUCCESS) |
276 | { |
277 | /* We found a passwd.adjunct.byname entry. Merge encrypted |
278 | password therein into original result. */ |
279 | char *encrypted = strchr (result2, ':'); |
280 | char *endp; |
281 | size_t restlen; |
282 | |
283 | if (encrypted == NULL |
284 | || (endp = strchr (++encrypted, ':')) == NULL |
285 | || (p = strchr (p + 1, ':')) == NULL) |
286 | { |
287 | /* Invalid format of the entry. This never should happen |
288 | unless the data from which the NIS table is generated is |
289 | wrong. We simply ignore it. */ |
290 | free (result2); |
291 | goto non_adjunct; |
292 | } |
293 | |
294 | restlen = len - (p - result); |
295 | if (__builtin_expect ((size_t) (namelen + (endp - encrypted) |
296 | + restlen + 2) > buflen, 0)) |
297 | { |
298 | free (result2); |
299 | free (result); |
300 | *errnop = ERANGE; |
301 | return NSS_STATUS_TRYAGAIN; |
302 | } |
303 | |
304 | mempcpy (mempcpy (mempcpy (mempcpy (buffer, result, namelen), |
305 | ":" , 1), |
306 | encrypted, endp - encrypted), |
307 | p, restlen + 1); |
308 | p = buffer; |
309 | |
310 | free (result2); |
311 | } |
312 | else |
313 | { |
314 | non_adjunct: |
315 | if (__glibc_unlikely ((size_t) (len + 1) > buflen)) |
316 | { |
317 | free (result); |
318 | *errnop = ERANGE; |
319 | return NSS_STATUS_TRYAGAIN; |
320 | } |
321 | |
322 | p = buffer; |
323 | *((char *) mempcpy (buffer, result, len)) = '\0'; |
324 | } |
325 | |
326 | while (isspace (*p)) |
327 | ++p; |
328 | if (!batch_read) |
329 | free (result); |
330 | |
331 | parse_res = _nss_files_parse_pwent (p, pwd, (void *) buffer, buflen, |
332 | errnop); |
333 | if (__glibc_unlikely (parse_res == -1)) |
334 | { |
335 | if (!batch_read) |
336 | free (outkey); |
337 | *errnop = ERANGE; |
338 | return NSS_STATUS_TRYAGAIN; |
339 | } |
340 | |
341 | if (batch_read) |
342 | intern.offset += len + 1; |
343 | else |
344 | { |
345 | free (oldkey); |
346 | oldkey = outkey; |
347 | oldkeylen = keylen; |
348 | new_start = false; |
349 | } |
350 | } |
351 | while (parse_res < 1); |
352 | |
353 | return NSS_STATUS_SUCCESS; |
354 | } |
355 | |
356 | enum nss_status |
357 | _nss_nis_getpwent_r (struct passwd *result, char *buffer, size_t buflen, |
358 | int *errnop) |
359 | { |
360 | int status; |
361 | |
362 | __libc_lock_lock (lock); |
363 | |
364 | status = internal_nis_getpwent_r (result, buffer, buflen, errnop); |
365 | |
366 | __libc_lock_unlock (lock); |
367 | |
368 | return status; |
369 | } |
370 | |
371 | enum nss_status |
372 | _nss_nis_getpwnam_r (const char *name, struct passwd *pwd, |
373 | char *buffer, size_t buflen, int *errnop) |
374 | { |
375 | if (name == NULL) |
376 | { |
377 | *errnop = EINVAL; |
378 | return NSS_STATUS_UNAVAIL; |
379 | } |
380 | |
381 | char *domain; |
382 | if (__glibc_unlikely (yp_get_default_domain (&domain))) |
383 | return NSS_STATUS_UNAVAIL; |
384 | |
385 | size_t namelen = strlen (name); |
386 | |
387 | char *result; |
388 | int len; |
389 | int yperr = yp_match (domain, "passwd.byname" , name, namelen, &result, &len); |
390 | |
391 | if (__glibc_unlikely (yperr != YPERR_SUCCESS)) |
392 | { |
393 | enum nss_status retval = yperr2nss (yperr); |
394 | |
395 | if (retval == NSS_STATUS_TRYAGAIN) |
396 | *errnop = errno; |
397 | return retval; |
398 | } |
399 | |
400 | /* Check for adjunct style secret passwords. They can be recognized |
401 | by a password starting with "##". We do not use it if the |
402 | passwd.adjunct.byname table is supposed to be used as a shadow.byname |
403 | replacement. */ |
404 | char *result2; |
405 | int len2; |
406 | char *p = strchr (result, ':'); |
407 | if ((_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW) == 0 |
408 | && p != NULL /* This better should be true in all cases. */ |
409 | && p[1] == '#' && p[2] == '#' |
410 | && yp_match (domain, "passwd.adjunct.byname" , name, namelen, |
411 | &result2, &len2) == YPERR_SUCCESS) |
412 | { |
413 | /* We found a passwd.adjunct.byname entry. Merge encrypted password |
414 | therein into original result. */ |
415 | char *encrypted = strchr (result2, ':'); |
416 | char *endp; |
417 | |
418 | if (encrypted == NULL |
419 | || (endp = strchr (++encrypted, ':')) == NULL |
420 | || (p = strchr (p + 1, ':')) == NULL) |
421 | { |
422 | /* Invalid format of the entry. This never should happen |
423 | unless the data from which the NIS table is generated is |
424 | wrong. We simply ignore it. */ |
425 | free (result2); |
426 | goto non_adjunct; |
427 | } |
428 | |
429 | size_t restlen = len - (p - result); |
430 | if (__builtin_expect ((size_t) (namelen + (endp - encrypted) |
431 | + restlen + 2) > buflen, 0)) |
432 | { |
433 | free (result2); |
434 | free (result); |
435 | *errnop = ERANGE; |
436 | return NSS_STATUS_TRYAGAIN; |
437 | } |
438 | |
439 | __mempcpy (__mempcpy (__mempcpy (__mempcpy (buffer, name, namelen), |
440 | ":" , 1), |
441 | encrypted, endp - encrypted), |
442 | p, restlen + 1); |
443 | p = buffer; |
444 | |
445 | free (result2); |
446 | } |
447 | else |
448 | { |
449 | non_adjunct: |
450 | if (__glibc_unlikely ((size_t) (len + 1) > buflen)) |
451 | { |
452 | free (result); |
453 | *errnop = ERANGE; |
454 | return NSS_STATUS_TRYAGAIN; |
455 | } |
456 | |
457 | p = strncpy (buffer, result, len); |
458 | buffer[len] = '\0'; |
459 | } |
460 | |
461 | while (isspace (*p)) |
462 | ++p; |
463 | free (result); |
464 | |
465 | int parse_res = _nss_files_parse_pwent (p, pwd, (void *) buffer, buflen, |
466 | errnop); |
467 | if (__glibc_unlikely (parse_res < 1)) |
468 | { |
469 | if (parse_res == -1) |
470 | return NSS_STATUS_TRYAGAIN; |
471 | else |
472 | return NSS_STATUS_NOTFOUND; |
473 | } |
474 | else |
475 | return NSS_STATUS_SUCCESS; |
476 | } |
477 | |
478 | enum nss_status |
479 | _nss_nis_getpwuid_r (uid_t uid, struct passwd *pwd, |
480 | char *buffer, size_t buflen, int *errnop) |
481 | { |
482 | char *domain; |
483 | if (__glibc_unlikely (yp_get_default_domain (&domain))) |
484 | return NSS_STATUS_UNAVAIL; |
485 | |
486 | char buf[32]; |
487 | int nlen = snprintf (buf, sizeof (buf), "%lu" , (unsigned long int) uid); |
488 | |
489 | char *result; |
490 | int len; |
491 | int yperr = yp_match (domain, "passwd.byuid" , buf, nlen, &result, &len); |
492 | |
493 | if (__glibc_unlikely (yperr != YPERR_SUCCESS)) |
494 | { |
495 | enum nss_status retval = yperr2nss (yperr); |
496 | |
497 | if (retval == NSS_STATUS_TRYAGAIN) |
498 | *errnop = errno; |
499 | return retval; |
500 | } |
501 | |
502 | /* Check for adjunct style secret passwords. They can be recognized |
503 | by a password starting with "##". We do not use it if the |
504 | passwd.adjunct.byname table is supposed to be used as a shadow.byname |
505 | replacement. */ |
506 | char *result2; |
507 | int len2; |
508 | size_t namelen; |
509 | char *p = strchr (result, ':'); |
510 | if ((_nsl_default_nss () & NSS_FLAG_ADJUNCT_AS_SHADOW) == 0 |
511 | && p != NULL /* This better should be true in all cases. */ |
512 | && p[1] == '#' && p[2] == '#' |
513 | && (namelen = p - result, |
514 | yp_match (domain, "passwd.adjunct.byname" , result, namelen, |
515 | &result2, &len2)) == YPERR_SUCCESS) |
516 | { |
517 | /* We found a passwd.adjunct.byname entry. Merge encrypted password |
518 | therein into original result. */ |
519 | char *encrypted = strchr (result2, ':'); |
520 | char *endp; |
521 | size_t restlen; |
522 | |
523 | if (encrypted == NULL |
524 | || (endp = strchr (++encrypted, ':')) == NULL |
525 | || (p = strchr (p + 1, ':')) == NULL) |
526 | { |
527 | /* Invalid format of the entry. This never should happen |
528 | unless the data from which the NIS table is generated is |
529 | wrong. We simply ignore it. */ |
530 | free (result2); |
531 | goto non_adjunct; |
532 | } |
533 | |
534 | restlen = len - (p - result); |
535 | if (__builtin_expect ((size_t) (namelen + (endp - encrypted) |
536 | + restlen + 2) > buflen, 0)) |
537 | { |
538 | free (result2); |
539 | free (result); |
540 | *errnop = ERANGE; |
541 | return NSS_STATUS_TRYAGAIN; |
542 | } |
543 | |
544 | __mempcpy (__mempcpy (__mempcpy (__mempcpy (buffer, result, namelen), |
545 | ":" , 1), |
546 | encrypted, endp - encrypted), |
547 | p, restlen + 1); |
548 | p = buffer; |
549 | |
550 | free (result2); |
551 | } |
552 | else |
553 | { |
554 | non_adjunct: |
555 | if (__glibc_unlikely ((size_t) (len + 1) > buflen)) |
556 | { |
557 | free (result); |
558 | *errnop = ERANGE; |
559 | return NSS_STATUS_TRYAGAIN; |
560 | } |
561 | |
562 | p = strncpy (buffer, result, len); |
563 | buffer[len] = '\0'; |
564 | } |
565 | |
566 | while (isspace (*p)) |
567 | ++p; |
568 | free (result); |
569 | |
570 | int parse_res = _nss_files_parse_pwent (p, pwd, (void *) buffer, buflen, |
571 | errnop); |
572 | if (__glibc_unlikely (parse_res < 1)) |
573 | { |
574 | if (parse_res == -1) |
575 | return NSS_STATUS_TRYAGAIN; |
576 | else |
577 | return NSS_STATUS_NOTFOUND; |
578 | } |
579 | else |
580 | return NSS_STATUS_SUCCESS; |
581 | } |
582 | |