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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
37 #include <nss_dbdefs.h>
41 #include <rpcsvc/nis.h>
43 #include "passwdutil.h"
48 * Translate the repository to a bitmask.
49 * if we don't recognise the repository name, we return REP_ERANGE
52 name_to_int(char *rep_name
)
54 int result
= REP_ERANGE
;
56 if (strcmp(rep_name
, "files") == 0)
58 else if (strcmp(rep_name
, "nis") == 0)
60 else if (strcmp(rep_name
, "ldap") == 0)
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
);
68 result
= REP_FILES
| REP_NIS
;
70 if (strcmp(cfg
->lookups
->service_name
, "ldap") == 0)
71 result
= REP_FILES
| REP_LDAP
;
74 (void) __nsw_freeconfig(cfg
);
82 * Figure out which repository we use in compat mode.
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
);
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 (needed to decode
111 * the black-magic '+' entries) but it translates into a bitmask
114 * If we detect read-access in compat mode, we augment the result
115 * with one of REP_COMPAT_{NIS,LDAP}. We need this in order to
116 * implement ATTR_REP_NAME in nss_getpwnam.
118 * A return value of REP_NOREP indicates an error.
121 get_ns(pwu_repository_t
*rep
, int accesstype
)
123 struct __nsw_switchconfig
*conf
= NULL
;
124 enum __nsw_parse_err pserr
;
125 struct __nsw_lookup
*lkp
;
126 struct __nsw_lookup
*lkp2
;
127 struct __nsw_lookup
*lkp3
;
128 struct __nsw_lookup
*lkpn
;
129 int result
= REP_NOREP
;
131 if (rep
!= PWU_DEFAULT_REP
) {
132 result
= name_to_int(rep
->type
);
136 conf
= __nsw_getconfig("passwd", &pserr
);
139 * No config found. The user didn't supply a repository,
140 * so we try to change the password in the default
141 * repositories (files and nis) even though we cannot
142 * find the name service switch entry. (Backward compat)
144 syslog(LOG_ERR
, "passwdutil.so: nameservice switch entry for "
145 "passwd not found.");
146 result
= REP_FILES
| REP_NIS
;
153 * Supported nsswitch.conf can have a maximum of 3 repositories.
154 * If we encounter an unsupported nsswitch.conf, we return REP_NSS
155 * to fall back to the nsswitch backend.
157 * Note that specifying 'ad' in the configuration is acceptable
158 * though changing AD users' passwords through passwd(1) is not.
159 * Therefore "ad" will be silently ignored.
161 if (conf
->num_lookups
== 1) {
162 /* files or compat */
164 if (strcmp(lkp
->service_name
, "files") == 0) {
165 result
= name_to_int(lkp
->service_name
);
166 } else if (strcmp(lkp
->service_name
, "compat") == 0) {
167 if (accesstype
== PWU_READ
)
168 result
= REP_NSS
| get_compat_mode();
170 result
= name_to_int(lkp
->service_name
);
174 } else if (conf
->num_lookups
== 2) {
176 if (strcmp(lkp
->service_name
, "files") == 0) {
178 if (strcmp(lkp2
->service_name
, "ldap") == 0)
180 else if (strcmp(lkp2
->service_name
, "nis") == 0)
182 else if (strcmp(lkp2
->service_name
, "ad") != 0)
188 } else if (conf
->num_lookups
== 3) {
190 * Valid configurations with 3 repositories are:
191 * files ad [nis | ldap ] OR
192 * files [nis | ldap ] ad
196 if (strcmp(lkp2
->service_name
, "ad") == 0)
198 else if (strcmp(lkp3
->service_name
, "ad") == 0)
202 if (strcmp(lkp
->service_name
, "files") == 0 &&
205 if (strcmp(lkpn
->service_name
, "ldap") == 0)
207 else if (strcmp(lkpn
->service_name
, "nis") == 0)
218 (void) __nsw_freeconfig(conf
);
226 p
->name
= NSS_DBNAM_PASSWD
;
227 p
->flags
|= NSS_USE_DEFAULT_CONFIG
;
228 p
->default_config
= "ldap";
235 p
->name
= NSS_DBNAM_SHADOW
;
236 p
->config_name
= NSS_DBNAM_PASSWD
; /* Use config for "passwd" */
237 p
->flags
|= NSS_USE_DEFAULT_CONFIG
;
238 p
->default_config
= "ldap";
247 p
->name
= NSS_DBNAM_PASSWD
;
248 p
->flags
|= NSS_USE_DEFAULT_CONFIG
;
249 p
->default_config
= "nis";
256 p
->name
= NSS_DBNAM_SHADOW
;
257 p
->config_name
= NSS_DBNAM_PASSWD
; /* Use config for "passwd" */
258 p
->flags
|= NSS_USE_DEFAULT_CONFIG
;
259 p
->default_config
= "nis";
274 while ((c
= *q
) != '\0' && c
!= ':') {
287 * Return values: 0 = success, 1 = parse error, 2 = erange ...
288 * The structure pointer passed in is a structure in the caller's space
289 * wherein the field pointers would be set to areas in the buffer if
290 * need be. instring and buffer should be separate areas.
293 str2passwd(const char *instr
, int lenstr
, void *ent
, char *buffer
, int buflen
)
295 struct passwd
*passwd
= (struct passwd
*)ent
;
297 int black_magic
; /* "+" or "-" entry */
299 if (lenstr
+ 1 > buflen
) {
300 return (NSS_STR_PARSE_ERANGE
);
303 * We copy the input string into the output buffer and
304 * operate on it in place.
306 (void) memcpy(buffer
, instr
, lenstr
);
307 buffer
[lenstr
] = '\0';
311 passwd
->pw_name
= p
= gettok(&next
); /* username */
313 /* Empty username; not allowed */
314 return (NSS_STR_PARSE_PARSE
);
316 black_magic
= (*p
== '+' || *p
== '-');
318 passwd
->pw_uid
= UID_NOBODY
;
319 passwd
->pw_gid
= GID_NOBODY
;
321 * pwconv tests pw_passwd and pw_age == NULL
323 passwd
->pw_passwd
= "";
326 * the rest of the passwd entry is "optional"
328 passwd
->pw_comment
= "";
329 passwd
->pw_gecos
= "";
331 passwd
->pw_shell
= "";
334 passwd
->pw_passwd
= p
= gettok(&next
); /* password */
337 return (NSS_STR_PARSE_SUCCESS
);
339 return (NSS_STR_PARSE_PARSE
);
341 for (; *p
!= '\0'; p
++) { /* age */
350 if (p
== 0 || *p
== '\0') {
352 return (NSS_STR_PARSE_SUCCESS
);
354 return (NSS_STR_PARSE_PARSE
);
357 passwd
->pw_uid
= strtol(p
, &next
, 10);
359 /* uid field should be nonempty */
360 return (NSS_STR_PARSE_PARSE
);
363 * The old code (in 2.0 thru 2.5) would check
364 * for the uid being negative, or being greater
365 * than 60001 (the rfs limit). If it met either of
366 * these conditions, the uid was translated to 60001.
368 * Now we just check for ephemeral uids; anything else
369 * is administrative policy
371 if (passwd
->pw_uid
> MAXUID
)
372 passwd
->pw_uid
= UID_NOBODY
;
374 if (*next
++ != ':') {
378 return (NSS_STR_PARSE_PARSE
);
381 if (p
== 0 || *p
== '\0') {
383 return (NSS_STR_PARSE_SUCCESS
);
385 return (NSS_STR_PARSE_PARSE
);
388 passwd
->pw_gid
= strtol(p
, &next
, 10);
390 /* gid field should be nonempty */
391 return (NSS_STR_PARSE_PARSE
);
394 * gid should be non-negative; anything else
395 * is administrative policy.
397 if (passwd
->pw_gid
> MAXUID
)
398 passwd
->pw_gid
= GID_NOBODY
;
400 if (*next
++ != ':') {
404 return (NSS_STR_PARSE_PARSE
);
407 passwd
->pw_gecos
= passwd
->pw_comment
= p
= gettok(&next
);
410 return (NSS_STR_PARSE_SUCCESS
);
412 return (NSS_STR_PARSE_PARSE
);
415 passwd
->pw_dir
= p
= gettok(&next
);
418 return (NSS_STR_PARSE_SUCCESS
);
420 return (NSS_STR_PARSE_PARSE
);
423 passwd
->pw_shell
= p
= gettok(&next
);
426 return (NSS_STR_PARSE_SUCCESS
);
428 return (NSS_STR_PARSE_PARSE
);
431 /* Better not be any more fields... */
433 /* Successfully parsed and stored */
434 return (NSS_STR_PARSE_SUCCESS
);
436 return (NSS_STR_PARSE_PARSE
);
439 typedef const char *constp
;
442 * Return value 1 means success and more input, 0 means error or no more
445 getfield(nextp
, limit
, uns
, valp
)
453 char numbuf
[12]; /* Holds -2^31 and trailing ':' */
458 if (p
== 0 || p
>= limit
) {
466 if ((len
= limit
- p
) > sizeof (numbuf
) - 1) {
467 len
= sizeof (numbuf
) - 1;
470 * We want to use strtol() and we have a readonly non-zero-terminated
471 * string, so first we copy and terminate the interesting bit.
472 * Ugh. (It's convenient to terminate with a colon rather than \0).
474 if ((endfield
= memccpy(numbuf
, p
, ':', len
)) == 0) {
475 if (len
!= limit
- p
) {
476 /* Error -- field is too big to be a legit number */
482 p
+= (endfield
- numbuf
);
485 ux
= strtoul(numbuf
, &endfield
, 10);
486 if (*endfield
!= ':') {
487 /* Error -- expected <integer><colon> */
490 *((unsigned int *)valp
) = (unsigned int)ux
;
492 x
= strtol(numbuf
, &endfield
, 10);
493 if (*endfield
!= ':') {
494 /* Error -- expected <integer><colon> */
497 *((int *)valp
) = (int)x
;
504 * str2spwd() -- convert a string to a shadow passwd entry. The parser is
505 * more liberal than the passwd or group parsers; since it's legitimate
506 * for almost all the fields here to be blank, the parser lets one omit
507 * any number of blank fields at the end of the entry. The acceptable
508 * forms for '+' and '-' entries are the same as those for normal entries.
509 * === Is this likely to do more harm than good?
511 * Return values: 0 = success, 1 = parse error, 2 = erange ...
512 * The structure pointer passed in is a structure in the caller's space
513 * wherein the field pointers would be set to areas in the buffer if
514 * need be. instring and buffer should be separate areas.
517 str2spwd(instr
, lenstr
, ent
, buffer
, buflen
)
520 void *ent
; /* really (struct spwd *) */
524 struct spwd
*shadow
= (struct spwd
*)ent
;
525 const char *p
= instr
, *limit
;
527 int lencopy
, black_magic
;
530 if ((p
= memchr(instr
, ':', lenstr
)) == 0 ||
532 (p
= memchr(p
, ':', limit
- p
)) == 0) {
539 if (lencopy
+ 1 > buflen
) {
540 return (NSS_STR_PARSE_ERANGE
);
542 (void) memcpy(buffer
, instr
, lencopy
);
545 black_magic
= (*instr
== '+' || *instr
== '-');
546 shadow
->sp_namp
= bufp
= buffer
;
548 shadow
->sp_lstchg
= -1;
551 shadow
->sp_warn
= -1;
552 shadow
->sp_inact
= -1;
553 shadow
->sp_expire
= -1;
556 if ((bufp
= strchr(bufp
, ':')) == 0) {
558 return (NSS_STR_PARSE_SUCCESS
);
560 return (NSS_STR_PARSE_PARSE
);
564 shadow
->sp_pwdp
= bufp
;
566 if ((bufp
= strchr(bufp
, ':')) == 0) {
568 return (NSS_STR_PARSE_SUCCESS
);
570 return (NSS_STR_PARSE_PARSE
);
574 } /* else p was set when we copied name and passwd into the buffer */
576 if (!getfield(&p
, limit
, 0, &shadow
->sp_lstchg
))
577 return (NSS_STR_PARSE_SUCCESS
);
578 if (!getfield(&p
, limit
, 0, &shadow
->sp_min
))
579 return (NSS_STR_PARSE_SUCCESS
);
580 if (!getfield(&p
, limit
, 0, &shadow
->sp_max
))
581 return (NSS_STR_PARSE_SUCCESS
);
582 if (!getfield(&p
, limit
, 0, &shadow
->sp_warn
))
583 return (NSS_STR_PARSE_SUCCESS
);
584 if (!getfield(&p
, limit
, 0, &shadow
->sp_inact
))
585 return (NSS_STR_PARSE_SUCCESS
);
586 if (!getfield(&p
, limit
, 0, &shadow
->sp_expire
))
587 return (NSS_STR_PARSE_SUCCESS
);
588 if (!getfield(&p
, limit
, 1, &shadow
->sp_flag
))
589 return (NSS_STR_PARSE_SUCCESS
);
591 /* Syntax error -- garbage at end of line */
592 return (NSS_STR_PARSE_PARSE
);
594 return (NSS_STR_PARSE_SUCCESS
);
597 static nss_XbyY_buf_t
*buffer
;
598 static DEFINE_NSS_DB_ROOT(db_root
);
601 NSS_XbyY_ALLOC(&buffer, sizeof (struct passwd), NSS_BUFLEN_PASSWD)
603 #pragma fini(endutilpwent)
608 NSS_XbyY_FREE(&buffer
);
609 nss_delete(&db_root
);
614 getpwnam_from(const char *name
, pwu_repository_t
*rep
, int reptype
)
616 nss_XbyY_buf_t
*b
= GETBUF();
622 NSS_XbyY_INIT(&arg
, b
->result
, b
->buffer
, b
->buflen
, str2passwd
);
627 (void) nss_search(&db_root
, nss_ldap_passwd
,
628 NSS_DBOP_PASSWD_BYNAME
, &arg
);
632 (void) nss_search(&db_root
, nss_nis_passwd
,
633 NSS_DBOP_PASSWD_BYNAME
, &arg
);
640 return (struct passwd
*)NSS_XbyY_FINI(&arg
);
645 getpwuid_from(uid_t uid
, pwu_repository_t
*rep
, int reptype
)
647 nss_XbyY_buf_t
*b
= GETBUF();
653 NSS_XbyY_INIT(&arg
, b
->result
, b
->buffer
, b
->buflen
, str2passwd
);
658 (void) nss_search(&db_root
, nss_ldap_passwd
,
659 NSS_DBOP_PASSWD_BYUID
, &arg
);
663 (void) nss_search(&db_root
, nss_nis_passwd
,
664 NSS_DBOP_PASSWD_BYUID
, &arg
);
671 return (struct passwd
*)NSS_XbyY_FINI(&arg
);
674 static nss_XbyY_buf_t
*spbuf
;
675 static DEFINE_NSS_DB_ROOT(spdb_root
);
678 NSS_XbyY_ALLOC(&spbuf, sizeof (struct spwd), NSS_BUFLEN_SHADOW)
680 #pragma fini(endutilspent)
685 NSS_XbyY_FREE(&spbuf
);
686 nss_delete(&spdb_root
);
691 getspnam_from(const char *name
, pwu_repository_t
*rep
, int reptype
)
693 nss_XbyY_buf_t
*b
= GETSPBUF();
699 NSS_XbyY_INIT(&arg
, b
->result
, b
->buffer
, b
->buflen
, str2spwd
);
703 (void) nss_search(&spdb_root
, nss_ldap_shadow
,
704 NSS_DBOP_SHADOW_BYNAME
, &arg
);
708 (void) nss_search(&spdb_root
, nss_nis_shadow
,
709 NSS_DBOP_SHADOW_BYNAME
, &arg
);
715 return (struct spwd
*)NSS_XbyY_FINI(&arg
);