dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / passwdutil / switch_utils.c
blob7d310a42750e45f5f217b910d3ddf3e215b6c1b4
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <nsswitch.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <stdlib.h>
34 #include <unistd.h>
36 #include "ns_sldap.h"
37 #include <nss_dbdefs.h>
38 #include <nsswitch.h>
39 #include <pwd.h>
40 #include <shadow.h>
41 #include <rpcsvc/nis.h>
43 #include "passwdutil.h"
46 * name_to_int(rep)
48 * Translate the repository to a bitmask.
49 * if we don't recognise the repository name, we return REP_ERANGE
51 int
52 name_to_int(char *rep_name)
54 int result = REP_ERANGE;
56 if (strcmp(rep_name, "files") == 0)
57 result = REP_FILES;
58 else if (strcmp(rep_name, "nis") == 0)
59 result = REP_NIS;
60 else if (strcmp(rep_name, "ldap") == 0)
61 result = REP_LDAP;
62 else if (strcmp(rep_name, "compat") == 0) {
63 struct __nsw_switchconfig *cfg;
64 enum __nsw_parse_err pserr;
66 cfg = __nsw_getconfig("passwd_compat", &pserr);
67 if (cfg == NULL) {
68 result = REP_FILES | REP_NIS;
69 } else {
70 if (strcmp(cfg->lookups->service_name, "ldap") == 0)
71 result = REP_FILES | REP_LDAP;
72 else
73 result = REP_ERANGE;
74 (void) __nsw_freeconfig(cfg);
78 return (result);
82 * Figure out which repository we use in compat mode.
84 int
85 get_compat_mode(void)
87 struct __nsw_switchconfig *cfg;
88 enum __nsw_parse_err pserr;
89 int result = REP_COMPAT_NIS;
91 if ((cfg = __nsw_getconfig("passwd_compat", &pserr)) != NULL) {
92 if (strcmp(cfg->lookups->service_name, "ldap") == 0)
93 result = REP_COMPAT_LDAP;
95 (void) __nsw_freeconfig(cfg);
97 return (result);
101 * get_ns(rep, accesstype)
103 * returns a bitmask of repositories to use based on either
104 * 1. the repository that is given as argument
105 * 2. the nsswitch.conf file
106 * 3. the type of access requested
108 * "accesstype" indicates whether we are reading from or writing to the
109 * repository. We need to know this since "compat" will translate into
110 * REP_NSS (the nss-switch) for READ access but it translates into a bitmask on
111 * WRITE access.
113 * If we detect read-access in compat mode, we augment the result
114 * with one of REP_COMPAT_{NIS,LDAP}. We need this in order to
115 * implement ATTR_REP_NAME in nss_getpwnam.
117 * A return value of REP_NOREP indicates an error.
120 get_ns(pwu_repository_t *rep, int accesstype)
122 struct __nsw_switchconfig *conf = NULL;
123 enum __nsw_parse_err pserr;
124 struct __nsw_lookup *lkp;
125 struct __nsw_lookup *lkp2;
126 struct __nsw_lookup *lkp3;
127 struct __nsw_lookup *lkpn;
128 int result = REP_NOREP;
130 if (rep != PWU_DEFAULT_REP) {
131 result = name_to_int(rep->type);
132 return (result);
135 conf = __nsw_getconfig("passwd", &pserr);
136 if (conf == NULL) {
138 * No config found. The user didn't supply a repository,
139 * so we try to change the password in the default
140 * repositories (files and nis) even though we cannot
141 * find the name service switch entry. (Backward compat)
143 syslog(LOG_ERR, "passwdutil.so: nameservice switch entry for "
144 "passwd not found.");
145 result = REP_FILES | REP_NIS;
146 return (result);
149 lkp = conf->lookups;
152 * Supported nsswitch.conf can have a maximum of 3 repositories.
153 * If we encounter an unsupported nsswitch.conf, we return REP_NSS
154 * to fall back to the nsswitch backend.
156 * Note that specifying 'ad' in the configuration is acceptable
157 * though changing AD users' passwords through passwd(1) is not.
158 * Therefore "ad" will be silently ignored.
160 if (conf->num_lookups == 1) {
161 /* files or compat */
163 if (strcmp(lkp->service_name, "files") == 0) {
164 result = name_to_int(lkp->service_name);
165 } else if (strcmp(lkp->service_name, "compat") == 0) {
166 if (accesstype == PWU_READ)
167 result = REP_NSS | get_compat_mode();
168 else
169 result = name_to_int(lkp->service_name);
170 } else
171 result = REP_NSS;
173 } else if (conf->num_lookups == 2) {
174 lkp2 = lkp->next;
175 if (strcmp(lkp->service_name, "files") == 0) {
176 result = REP_FILES;
177 if (strcmp(lkp2->service_name, "ldap") == 0)
178 result |= REP_LDAP;
179 else if (strcmp(lkp2->service_name, "nis") == 0)
180 result |= REP_NIS;
181 else if (strcmp(lkp2->service_name, "ad") != 0)
182 result = REP_NSS;
183 /* AD is ignored */
184 } else {
185 result = REP_NSS;
187 } else if (conf->num_lookups == 3) {
189 * Valid configurations with 3 repositories are:
190 * files ad [nis | ldap ] OR
191 * files [nis | ldap ] ad
193 lkp2 = lkp->next;
194 lkp3 = lkp2->next;
195 if (strcmp(lkp2->service_name, "ad") == 0)
196 lkpn = lkp3;
197 else if (strcmp(lkp3->service_name, "ad") == 0)
198 lkpn = lkp2;
199 else
200 lkpn = NULL;
201 if (strcmp(lkp->service_name, "files") == 0 &&
202 lkpn != NULL) {
203 result = REP_FILES;
204 if (strcmp(lkpn->service_name, "ldap") == 0)
205 result |= REP_LDAP;
206 else if (strcmp(lkpn->service_name, "nis") == 0)
207 result |= REP_NIS;
208 else
209 result = REP_NSS;
210 } else {
211 result = REP_NSS;
213 } else {
214 result = REP_NSS;
217 (void) __nsw_freeconfig(conf);
218 return (result);
221 static void
222 nss_ldap_passwd(p)
223 nss_db_params_t *p;
225 p->name = NSS_DBNAM_PASSWD;
226 p->flags |= NSS_USE_DEFAULT_CONFIG;
227 p->default_config = "ldap";
230 static void
231 nss_ldap_shadow(p)
232 nss_db_params_t *p;
234 p->name = NSS_DBNAM_SHADOW;
235 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */
236 p->flags |= NSS_USE_DEFAULT_CONFIG;
237 p->default_config = "ldap";
241 #ifdef PAM_NIS
242 static void
243 nss_nis_passwd(p)
244 nss_db_params_t *p;
246 p->name = NSS_DBNAM_PASSWD;
247 p->flags |= NSS_USE_DEFAULT_CONFIG;
248 p->default_config = "nis";
251 static void
252 nss_nis_shadow(p)
253 nss_db_params_t *p;
255 p->name = NSS_DBNAM_SHADOW;
256 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */
257 p->flags |= NSS_USE_DEFAULT_CONFIG;
258 p->default_config = "nis";
260 #endif /* PAM_NIS */
262 static char *
263 gettok(nextpp)
264 char **nextpp;
266 char *p = *nextpp;
267 char *q = p;
268 char c;
270 if (p == 0) {
271 return (0);
273 while ((c = *q) != '\0' && c != ':') {
274 q++;
276 if (c == '\0') {
277 *nextpp = 0;
278 } else {
279 *q++ = '\0';
280 *nextpp = q;
282 return (p);
286 * Return values: 0 = success, 1 = parse error, 2 = erange ...
287 * The structure pointer passed in is a structure in the caller's space
288 * wherein the field pointers would be set to areas in the buffer if
289 * need be. instring and buffer should be separate areas.
291 static int
292 str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
294 struct passwd *passwd = (struct passwd *)ent;
295 char *p, *next;
297 if (lenstr + 1 > buflen) {
298 return (NSS_STR_PARSE_ERANGE);
301 * We copy the input string into the output buffer and
302 * operate on it in place.
304 (void) memcpy(buffer, instr, lenstr);
305 buffer[lenstr] = '\0';
307 next = buffer;
309 passwd->pw_name = p = gettok(&next); /* username */
310 if (*p == '\0') {
311 /* Empty username; not allowed */
312 return (NSS_STR_PARSE_PARSE);
315 passwd->pw_passwd = p = gettok(&next); /* password */
316 if (p == 0)
317 return (NSS_STR_PARSE_PARSE);
318 for (; *p != '\0'; p++) { /* age */
319 if (*p == ',') {
320 *p++ = '\0';
321 break;
324 passwd->pw_age = p;
326 p = next; /* uid */
327 if (p == 0 || *p == '\0')
328 return (NSS_STR_PARSE_PARSE);
330 passwd->pw_uid = strtol(p, &next, 10);
331 if (next == p) {
332 /* uid field should be nonempty */
333 return (NSS_STR_PARSE_PARSE);
336 * The old code (in 2.0 thru 2.5) would check
337 * for the uid being negative, or being greater
338 * than 60001 (the rfs limit). If it met either of
339 * these conditions, the uid was translated to 60001.
341 * Now we just check for ephemeral uids; anything else
342 * is administrative policy
344 if (passwd->pw_uid > MAXUID)
345 passwd->pw_uid = UID_NOBODY;
347 if (*next++ != ':')
348 return (NSS_STR_PARSE_PARSE);
349 p = next; /* gid */
350 if (p == 0 || *p == '\0')
351 return (NSS_STR_PARSE_PARSE);
353 passwd->pw_gid = strtol(p, &next, 10);
354 if (next == p) {
355 /* gid field should be nonempty */
356 return (NSS_STR_PARSE_PARSE);
359 * gid should be non-negative; anything else
360 * is administrative policy.
362 if (passwd->pw_gid > MAXUID)
363 passwd->pw_gid = GID_NOBODY;
364 if (*next++ != ':')
365 return (NSS_STR_PARSE_PARSE);
367 passwd->pw_gecos = passwd->pw_comment = p = gettok(&next);
368 if (p == 0)
369 return (NSS_STR_PARSE_PARSE);
371 passwd->pw_dir = p = gettok(&next);
372 if (p == 0)
373 return (NSS_STR_PARSE_PARSE);
375 passwd->pw_shell = p = gettok(&next);
376 if (p == 0)
377 return (NSS_STR_PARSE_PARSE);
379 /* Better not be any more fields... */
380 if (next == 0) {
381 /* Successfully parsed and stored */
382 return (NSS_STR_PARSE_SUCCESS);
384 return (NSS_STR_PARSE_PARSE);
387 typedef const char *constp;
390 * Return value 1 means success and more input, 0 means error or no more
392 static int
393 getfield(nextp, limit, uns, valp)
394 constp *nextp;
395 constp limit;
396 int uns;
397 void *valp;
399 constp p = *nextp;
400 char *endfield;
401 char numbuf[12]; /* Holds -2^31 and trailing ':' */
402 int len;
403 long x;
404 unsigned long ux;
406 if (p == 0 || p >= limit) {
407 return (0);
409 if (*p == ':') {
410 p++;
411 *nextp = p;
412 return (p < limit);
414 if ((len = limit - p) > sizeof (numbuf) - 1) {
415 len = sizeof (numbuf) - 1;
418 * We want to use strtol() and we have a readonly non-zero-terminated
419 * string, so first we copy and terminate the interesting bit.
420 * Ugh. (It's convenient to terminate with a colon rather than \0).
422 if ((endfield = memccpy(numbuf, p, ':', len)) == 0) {
423 if (len != limit - p) {
424 /* Error -- field is too big to be a legit number */
425 return (0);
427 numbuf[len] = ':';
428 p = limit;
429 } else {
430 p += (endfield - numbuf);
432 if (uns) {
433 ux = strtoul(numbuf, &endfield, 10);
434 if (*endfield != ':') {
435 /* Error -- expected <integer><colon> */
436 return (0);
438 *((unsigned int *)valp) = (unsigned int)ux;
439 } else {
440 x = strtol(numbuf, &endfield, 10);
441 if (*endfield != ':') {
442 /* Error -- expected <integer><colon> */
443 return (0);
445 *((int *)valp) = (int)x;
447 *nextp = p;
448 return (p < limit);
452 * str2spwd() -- convert a string to a shadow passwd entry. The parser is
453 * more liberal than the passwd or group parsers; since it's legitimate
454 * for almost all the fields here to be blank, the parser lets one omit
455 * any number of blank fields at the end of the entry.
456 * === Is this likely to do more harm than good?
458 * Return values: 0 = success, 1 = parse error, 2 = erange ...
459 * The structure pointer passed in is a structure in the caller's space
460 * wherein the field pointers would be set to areas in the buffer if
461 * need be. instring and buffer should be separate areas.
464 str2spwd(instr, lenstr, ent, buffer, buflen)
465 const char *instr;
466 int lenstr;
467 void *ent; /* really (struct spwd *) */
468 char *buffer;
469 int buflen;
471 struct spwd *shadow = (struct spwd *)ent;
472 const char *p = instr, *limit;
473 char *bufp;
474 int lencopy;
476 limit = p + lenstr;
477 if ((p = memchr(instr, ':', lenstr)) == 0 ||
478 ++p >= limit ||
479 (p = memchr(p, ':', limit - p)) == 0) {
480 lencopy = lenstr;
481 p = 0;
482 } else {
483 lencopy = p - instr;
484 p++;
486 if (lencopy + 1 > buflen) {
487 return (NSS_STR_PARSE_ERANGE);
489 (void) memcpy(buffer, instr, lencopy);
490 buffer[lencopy] = 0;
492 shadow->sp_namp = bufp = buffer;
493 shadow->sp_pwdp = 0;
494 shadow->sp_lstchg = -1;
495 shadow->sp_min = -1;
496 shadow->sp_max = -1;
497 shadow->sp_warn = -1;
498 shadow->sp_inact = -1;
499 shadow->sp_expire = -1;
500 shadow->sp_flag = 0;
502 if ((bufp = strchr(bufp, ':')) == 0)
503 return (NSS_STR_PARSE_PARSE);
504 *bufp++ = '\0';
506 shadow->sp_pwdp = bufp;
507 if (instr == 0) {
508 if ((bufp = strchr(bufp, ':')) == 0)
509 return (NSS_STR_PARSE_PARSE);
510 *bufp++ = '\0';
511 p = bufp;
512 } /* else p was set when we copied name and passwd into the buffer */
514 if (!getfield(&p, limit, 0, &shadow->sp_lstchg))
515 return (NSS_STR_PARSE_SUCCESS);
516 if (!getfield(&p, limit, 0, &shadow->sp_min))
517 return (NSS_STR_PARSE_SUCCESS);
518 if (!getfield(&p, limit, 0, &shadow->sp_max))
519 return (NSS_STR_PARSE_SUCCESS);
520 if (!getfield(&p, limit, 0, &shadow->sp_warn))
521 return (NSS_STR_PARSE_SUCCESS);
522 if (!getfield(&p, limit, 0, &shadow->sp_inact))
523 return (NSS_STR_PARSE_SUCCESS);
524 if (!getfield(&p, limit, 0, &shadow->sp_expire))
525 return (NSS_STR_PARSE_SUCCESS);
526 if (!getfield(&p, limit, 1, &shadow->sp_flag))
527 return (NSS_STR_PARSE_SUCCESS);
528 if (p != limit) {
529 /* Syntax error -- garbage at end of line */
530 return (NSS_STR_PARSE_PARSE);
532 return (NSS_STR_PARSE_SUCCESS);
535 static nss_XbyY_buf_t *buffer;
536 static DEFINE_NSS_DB_ROOT(db_root);
538 #define GETBUF() \
539 NSS_XbyY_ALLOC(&buffer, sizeof (struct passwd), NSS_BUFLEN_PASSWD)
541 #pragma fini(endutilpwent)
543 static void
544 endutilpwent(void)
546 NSS_XbyY_FREE(&buffer);
547 nss_delete(&db_root);
550 /*ARGSUSED*/
551 struct passwd *
552 getpwnam_from(const char *name, pwu_repository_t *rep, int reptype)
554 nss_XbyY_buf_t *b = GETBUF();
555 nss_XbyY_args_t arg;
557 if (b == 0)
558 return (0);
560 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd);
561 arg.key.name = name;
563 switch (reptype) {
564 case REP_LDAP:
565 (void) nss_search(&db_root, nss_ldap_passwd,
566 NSS_DBOP_PASSWD_BYNAME, &arg);
567 break;
568 #ifdef PAM_NIS
569 case REP_NIS:
570 (void) nss_search(&db_root, nss_nis_passwd,
571 NSS_DBOP_PASSWD_BYNAME, &arg);
572 break;
573 #endif
574 default:
575 return (NULL);
578 return (struct passwd *)NSS_XbyY_FINI(&arg);
581 /*ARGSUSED*/
582 struct passwd *
583 getpwuid_from(uid_t uid, pwu_repository_t *rep, int reptype)
585 nss_XbyY_buf_t *b = GETBUF();
586 nss_XbyY_args_t arg;
588 if (b == 0)
589 return (0);
591 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd);
592 arg.key.uid = uid;
594 switch (reptype) {
595 case REP_LDAP:
596 (void) nss_search(&db_root, nss_ldap_passwd,
597 NSS_DBOP_PASSWD_BYUID, &arg);
598 break;
599 #ifdef PAM_NIS
600 case REP_NIS:
601 (void) nss_search(&db_root, nss_nis_passwd,
602 NSS_DBOP_PASSWD_BYUID, &arg);
603 break;
604 #endif
605 default:
606 return (NULL);
609 return (struct passwd *)NSS_XbyY_FINI(&arg);
612 static nss_XbyY_buf_t *spbuf;
613 static DEFINE_NSS_DB_ROOT(spdb_root);
615 #define GETSPBUF() \
616 NSS_XbyY_ALLOC(&spbuf, sizeof (struct spwd), NSS_BUFLEN_SHADOW)
618 #pragma fini(endutilspent)
620 static void
621 endutilspent(void)
623 NSS_XbyY_FREE(&spbuf);
624 nss_delete(&spdb_root);
627 /*ARGSUSED*/
628 struct spwd *
629 getspnam_from(const char *name, pwu_repository_t *rep, int reptype)
631 nss_XbyY_buf_t *b = GETSPBUF();
632 nss_XbyY_args_t arg;
634 if (b == 0)
635 return (0);
637 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2spwd);
638 arg.key.name = name;
639 switch (reptype) {
640 case REP_LDAP:
641 (void) nss_search(&spdb_root, nss_ldap_shadow,
642 NSS_DBOP_SHADOW_BYNAME, &arg);
643 break;
644 #ifdef PAM_NIS
645 case REP_NIS:
646 (void) nss_search(&spdb_root, nss_nis_shadow,
647 NSS_DBOP_SHADOW_BYNAME, &arg);
648 break;
649 #endif
650 default:
651 return (NULL);
653 return (struct spwd *)NSS_XbyY_FINI(&arg);