2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: switch_utils.c /main/5 1996/05/09 04:32:20 drk $ */
26 * Copyright (c) 1992-1995, by Sun Microsystems, Inc.
27 * All rights reserved.
30 #ident "@(#)switch_utils.c 1.31 96/02/02 SMI"
32 #include "unix_headers.h"
36 * This file relies on the fact that you are using NSS_SWITCH.
37 * If PAM_NIS or PAM_NISPLUS is defined, then the
38 * include files for nss_switch are included.
39 * Otherwise, the repository is assumed to be FILES,
40 * and NSS_SWITCH is not used.
43 static void pr_config();
46 * The following is similar to getpwnam() except that it specifies
47 * where to get the information. This is modeled after getpwnam_r().
54 p->name = NSS_DBNAM_PASSWD;
55 p->flags |= NSS_USE_DEFAULT_CONFIG;
56 p->default_config = "nis";
63 p->name = NSS_DBNAM_SHADOW;
64 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */
65 p->flags |= NSS_USE_DEFAULT_CONFIG;
66 p->default_config = "nis";
75 p->name = NSS_DBNAM_PASSWD;
76 p->flags |= NSS_USE_DEFAULT_CONFIG;
77 p->default_config = "nisplus";
84 p->name = NSS_DBNAM_SHADOW;
85 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */
86 p->flags |= NSS_USE_DEFAULT_CONFIG;
87 p->default_config = "nisplus";
89 #endif /* PAM_NISPLUS */
102 while ((c = *q) != '\0' && c != ':') {
114 #if (PAM_NIS || PAM_NISPLUS)
117 * Return values: 0 = success, 1 = parse error, 2 = erange ...
118 * The structure pointer passed in is a structure in the caller's space
119 * wherein the field pointers would be set to areas in the buffer if
120 * need be. instring and buffer should be separate areas.
123 str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
125 struct passwd *passwd = (struct passwd *)ent;
127 int black_magic; /* "+" or "-" entry */
129 if (lenstr + 1 > buflen) {
130 return (NSS_STR_PARSE_ERANGE);
133 * We copy the input string into the output buffer and
134 * operate on it in place.
136 (void) memcpy(buffer, instr, lenstr);
137 buffer[lenstr] = '\0';
141 passwd->pw_name = p = gettok(&next); /* username */
143 /* Empty username; not allowed */
144 return (NSS_STR_PARSE_PARSE);
146 black_magic = (*p == '+' || *p == '-');
148 passwd->pw_uid = UID_NOBODY;
149 passwd->pw_gid = GID_NOBODY;
151 * pwconv tests pw_passwd and pw_age == NULL
153 passwd->pw_passwd = "";
156 * the rest of the passwd entry is "optional"
158 passwd->pw_comment = "";
159 passwd->pw_gecos = "";
161 passwd->pw_shell = "";
164 passwd->pw_passwd = p = gettok(&next); /* password */
167 return (NSS_STR_PARSE_SUCCESS);
169 return (NSS_STR_PARSE_PARSE);
171 for (; *p != '\0'; p++) { /* age */
180 if (p == 0 || *p == '\0') {
182 return (NSS_STR_PARSE_SUCCESS);
184 return (NSS_STR_PARSE_PARSE);
187 passwd->pw_uid = strtol(p, &next, 10);
189 /* uid field should be nonempty */
190 return (NSS_STR_PARSE_PARSE);
193 * The old code (in 2.0 thru 2.5) would check
194 * for the uid being negative, or being greater
195 * than 60001 (the rfs limit). If it met either of
196 * these conditions, the uid was translated to 60001.
198 * Now we just check for negative uids; anything else
199 * is administrative policy
201 if (passwd->pw_uid < 0)
202 passwd->pw_uid = UID_NOBODY;
204 if (*next++ != ':') {
208 return (NSS_STR_PARSE_PARSE);
211 if (p == 0 || *p == '\0') {
213 return (NSS_STR_PARSE_SUCCESS);
215 return (NSS_STR_PARSE_PARSE);
218 passwd->pw_gid = strtol(p, &next, 10);
220 /* gid field should be nonempty */
221 return (NSS_STR_PARSE_PARSE);
224 * gid should be non-negative; anything else
225 * is administrative policy.
227 if (passwd->pw_gid < 0)
228 passwd->pw_gid = GID_NOBODY;
230 if (*next++ != ':') {
234 return (NSS_STR_PARSE_PARSE);
237 passwd->pw_gecos = passwd->pw_comment = p = gettok(&next);
240 return (NSS_STR_PARSE_SUCCESS);
242 return (NSS_STR_PARSE_PARSE);
245 passwd->pw_dir = p = gettok(&next);
248 return (NSS_STR_PARSE_SUCCESS);
250 return (NSS_STR_PARSE_PARSE);
253 passwd->pw_shell = p = gettok(&next);
256 return (NSS_STR_PARSE_SUCCESS);
258 return (NSS_STR_PARSE_PARSE);
261 /* Better not be any more fields... */
263 /* Successfully parsed and stored */
264 return (NSS_STR_PARSE_SUCCESS);
266 return (NSS_STR_PARSE_PARSE);
269 typedef const char *constp;
271 static bool_t /* 1 means success and more input, 0 means error or no more */
272 getfield(nextp, limit, uns, valp)
280 char numbuf[12]; /* Holds -2^31 and trailing \0 */
283 if (p == 0 || p >= limit) {
291 if ((len = limit - p) > sizeof (numbuf) - 1) {
292 len = sizeof (numbuf) - 1;
295 * We want to use strtol() and we have a readonly non-zero-terminated
296 * string, so first we copy and terminate the interesting bit.
297 * Ugh. (It's convenient to terminate with a colon rather than \0).
299 if ((endfield = memccpy(numbuf, p, ':', len)) == 0) {
300 if (len != limit - p) {
301 /* Error -- field is too big to be a legit number */
307 p += (endfield - numbuf);
310 *((unsigned long *)valp) = strtoul(numbuf, &endfield, 10);
312 *((long *)valp) = strtol(numbuf, &endfield, 10);
314 if (*endfield != ':') {
315 /* Error -- expected <integer><colon>, got something else */
323 * str2spwd() -- convert a string to a shadow passwd entry. The parser is
324 * more liberal than the passwd or group parsers; since it's legitimate
325 * for almost all the fields here to be blank, the parser lets one omit
326 * any number of blank fields at the end of the entry. The acceptable
327 * forms for '+' and '-' entries are the same as those for normal entries.
328 * === Is this likely to do more harm than good?
330 * Return values: 0 = success, 1 = parse error, 2 = erange ...
331 * The structure pointer passed in is a structure in the caller's space
332 * wherein the field pointers would be set to areas in the buffer if
333 * need be. instring and buffer should be separate areas.
336 str2spwd(instr, lenstr, ent, buffer, buflen)
339 void *ent; /* really (struct spwd *) */
343 struct spwd *shadow = (struct spwd *)ent;
344 const char *p = instr, *limit;
346 int lencopy, black_magic;
349 if ((p = memchr(instr, ':', lenstr)) == 0 ||
351 (p = memchr(p, ':', limit - p)) == 0) {
358 if (lencopy + 1 > buflen) {
359 return (NSS_STR_PARSE_ERANGE);
361 (void) memcpy(buffer, instr, lencopy);
364 black_magic = (*instr == '+' || *instr == '-');
365 shadow->sp_namp = bufp = buffer;
367 shadow->sp_lstchg = -1;
370 shadow->sp_warn = -1;
371 shadow->sp_inact = -1;
372 shadow->sp_expire = -1;
375 if ((bufp = strchr(bufp, ':')) == 0) {
377 return (NSS_STR_PARSE_SUCCESS);
379 return (NSS_STR_PARSE_PARSE);
383 shadow->sp_pwdp = bufp;
385 if ((bufp = strchr(bufp, ':')) == 0) {
387 return (NSS_STR_PARSE_SUCCESS);
389 return (NSS_STR_PARSE_PARSE);
393 } /* else p was set when we copied name and passwd into the buffer */
395 if (!getfield(&p, limit, 0, &shadow->sp_lstchg))
396 return (NSS_STR_PARSE_SUCCESS);
397 if (!getfield(&p, limit, 0, &shadow->sp_min))
398 return (NSS_STR_PARSE_SUCCESS);
399 if (!getfield(&p, limit, 0, &shadow->sp_max))
400 return (NSS_STR_PARSE_SUCCESS);
401 if (!getfield(&p, limit, 0, &shadow->sp_warn))
402 return (NSS_STR_PARSE_SUCCESS);
403 if (!getfield(&p, limit, 0, &shadow->sp_inact))
404 return (NSS_STR_PARSE_SUCCESS);
405 if (!getfield(&p, limit, 0, &shadow->sp_expire))
406 return (NSS_STR_PARSE_SUCCESS);
407 if (!getfield(&p, limit, 1, &shadow->sp_flag))
408 return (NSS_STR_PARSE_SUCCESS);
410 /* Syntax error -- garbage at end of line */
411 return (NSS_STR_PARSE_PARSE);
413 return (NSS_STR_PARSE_SUCCESS);
416 static nss_XbyY_buf_t *buffer;
417 static DEFINE_NSS_DB_ROOT(db_root);
420 NSS_XbyY_ALLOC(&buffer, sizeof (struct passwd), NSS_BUFLEN_PASSWD)
423 getpwnam_from(name, rep)
427 nss_XbyY_buf_t *b = GETBUF();
433 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd);
438 case PAM_REP_NISPLUS:
439 nss_search(&db_root, nss_nisplus_passwd, NSS_DBOP_PASSWD_BYNAME,
445 nss_search(&db_root, nss_nis_passwd, NSS_DBOP_PASSWD_BYNAME,
453 return (struct passwd *) NSS_XbyY_FINI(&arg);
456 static nss_XbyY_buf_t *spbuf;
457 static DEFINE_NSS_DB_ROOT(spdb_root);
460 NSS_XbyY_ALLOC(&spbuf, sizeof (struct spwd), NSS_BUFLEN_SHADOW)
463 getspnam_from(name, rep)
467 nss_XbyY_buf_t *b = GETSPBUF();
473 NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2spwd);
477 case PAM_REP_NISPLUS:
478 nss_search(&spdb_root, nss_nisplus_shadow,
479 NSS_DBOP_SHADOW_BYNAME, &arg);
484 nss_search(&spdb_root, nss_nis_shadow,
485 NSS_DBOP_SHADOW_BYNAME, &arg);
491 return (struct spwd *) NSS_XbyY_FINI(&arg);
493 #endif /* (PAM_NIS || PAM_NISPLUS) */
499 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
501 sprintf(messages[0], PAM_MSG(pamh, 1,
502 "Supported configurations for passwd management are as follows:"));
503 sprintf(messages[1], PAM_MSG(pamh, 2, " passwd: files"));
504 sprintf(messages[2], PAM_MSG(pamh, 3, " passwd: files nis"));
505 sprintf(messages[3], PAM_MSG(pamh, 4, " passwd: files nisplus"));
506 sprintf(messages[4], PAM_MSG(pamh, 5, " passwd: compat"));
507 sprintf(messages[5], PAM_MSG(pamh, 6, " passwd: compat AND"));
508 sprintf(messages[6], PAM_MSG(pamh, 7, " passwd_compat: nisplus"));
510 PAM_MSG(pamh, 8, "Please check your /etc/nsswitch.conf file"));
512 /* display the above 8 messages */
513 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 8, messages, NULL);
517 * get name services (or repositories) of passwd.
518 * o_rep: the specified respository in command line. If no repository is
519 * specified in the command line, o_rep is equal to PAM_REP_DEFAULT.
520 * return value: new repositories
521 * 1. In the case of PAM_REP_DEFAULT, new repositories are from nsswitch
522 * file (as long as it represents a valid and supported configuration).
523 * 2. In the case of specified repository, it should be present as one
524 * of the valid services (or repositories) in nsswitch file.
525 * A warning is printed if this happens. Operation is continued.
528 get_ns(pamh, o_rep, debug, nowarn)
534 #if (PAM_NIS || PAM_NISPLUS)
535 struct __nsw_switchconfig *conf = NULL;
536 struct __nsw_switchconfig *confcomp = NULL;
537 enum __nsw_parse_err pserr;
538 struct __nsw_lookup *lkp;
539 struct __nsw_lookup *lkp2;
541 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
543 /* yppasswd/nispasswd doesn't care about nsswitch file */
544 if (IS_OPWCMD(o_rep))
547 conf = __nsw_getconfig("passwd", &pserr);
550 sprintf(messages[0], PAM_MSG(pamh, 9,
551 "Can't find name service for passwd"));
552 __pam_display_msg(pamh, PAM_ERROR_MSG,
556 if (IS_NISPLUS(o_rep)) {
558 sprintf(messages[0], PAM_MSG(pamh, 10,
559 "You may not use nisplus repository"));
560 __pam_display_msg(pamh, PAM_ERROR_MSG,
564 } else if (o_rep != PAM_REP_DEFAULT) {
566 * The user specified a repository:
567 * Allow the user to try to change the passwd
568 * even though the specified repository is
569 * not listed in nsswitch.conf
574 * The user did not specify a repository:
575 * Allow the user to try to change the passwd
576 * in the default repositories (files and nis)
577 * even though we can not find the name service
580 rep = PAM_REP_FILES | PAM_REP_NIS;
581 return (rep); /* default */
586 syslog(LOG_DEBUG, "number of services is %d",
591 * XXX: Currently we do now support more than 2 services
593 if (conf->num_lookups > 2) {
596 } else if (conf->num_lookups == 1) {
597 /* files or compat */
598 if (strcmp(lkp->service_name, "files") == 0) {
599 rep |= PAM_REP_FILES;
600 if (o_rep == PAM_REP_NIS || o_rep == PAM_REP_NISPLUS) {
604 "Your specified repository is not defined in the nsswitch file!"));
605 __pam_display_msg(pamh, PAM_ERROR_MSG,
611 } else if (strcmp(lkp->service_name, "compat") == 0) {
612 /* get passwd_compat */
613 confcomp = __nsw_getconfig("passwd_compat", &pserr);
614 if (confcomp == NULL) {
615 rep = PAM_REP_FILES | PAM_REP_NIS;
616 if (o_rep == PAM_REP_NISPLUS) {
620 "Your specified repository is not defined in the nsswitch file!"));
621 __pam_display_msg(pamh, PAM_ERROR_MSG,
625 } else if (o_rep != PAM_REP_DEFAULT)
630 /* check the service: nisplus? */
631 if (strcmp(confcomp->lookups->service_name,
633 rep = PAM_REP_FILES | PAM_REP_NISPLUS;
634 if (o_rep == PAM_REP_NIS) {
638 "Your specified repository is not defined in the nsswitch file!"));
639 __pam_display_msg(pamh,
644 } else if (o_rep != PAM_REP_DEFAULT)
649 /* passwd_compat must be nisplus?? */
657 } else { /* two services */
660 if (strcmp(lkp->service_name, "files") == 0) {
661 /* files nis, or files nisplus */
662 rep |= PAM_REP_FILES;
668 if (strcmp(lkp2->service_name, "nis") == 0) {
670 if (o_rep == PAM_REP_NISPLUS) {
674 "Your specified repository is not defined in the nsswitch file!"));
675 __pam_display_msg(pamh, PAM_ERROR_MSG,
679 } else if (o_rep != PAM_REP_DEFAULT)
683 } else if (strcmp(lkp2->service_name, "nisplus") == 0) {
684 rep |= PAM_REP_NISPLUS;
685 if (o_rep == PAM_REP_NIS) {
689 "Your specified repository is not defined in the nsswitch file!"));
690 __pam_display_msg(pamh, PAM_ERROR_MSG,
694 } else if (o_rep != PAM_REP_DEFAULT)
704 return (PAM_REP_FILES);
705 #endif /* (PAM_NIS || PAM_NISPLUS) */