xref: /onnv-gate/usr/src/lib/passwdutil/switch_utils.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <nsswitch.h>
31*0Sstevel@tonic-gate #include <stdlib.h>
32*0Sstevel@tonic-gate #include <stdio.h>
33*0Sstevel@tonic-gate #include <string.h>
34*0Sstevel@tonic-gate #include <syslog.h>
35*0Sstevel@tonic-gate #include <stdlib.h>
36*0Sstevel@tonic-gate #include <unistd.h>
37*0Sstevel@tonic-gate 
38*0Sstevel@tonic-gate #include "ns_sldap.h"
39*0Sstevel@tonic-gate #include <nss_dbdefs.h>
40*0Sstevel@tonic-gate #include <nsswitch.h>
41*0Sstevel@tonic-gate #include <pwd.h>
42*0Sstevel@tonic-gate #include <shadow.h>
43*0Sstevel@tonic-gate #include <rpcsvc/nis.h>
44*0Sstevel@tonic-gate 
45*0Sstevel@tonic-gate #include "passwdutil.h"
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate static struct passwd *nisplus_getpw_from_master(const char *, char *);
48*0Sstevel@tonic-gate static struct spwd *nisplus_getsp_from_master(const char *, char *);
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate /*
51*0Sstevel@tonic-gate  * name_to_int(rep)
52*0Sstevel@tonic-gate  *
53*0Sstevel@tonic-gate  * Translate the repository to a bitmask.
54*0Sstevel@tonic-gate  * if we don't recognise the repository name, we return REP_ERANGE
55*0Sstevel@tonic-gate  */
56*0Sstevel@tonic-gate int
57*0Sstevel@tonic-gate name_to_int(char *rep_name)
58*0Sstevel@tonic-gate {
59*0Sstevel@tonic-gate 	int result = REP_ERANGE;
60*0Sstevel@tonic-gate 
61*0Sstevel@tonic-gate 	if (strcmp(rep_name, "files") == 0)
62*0Sstevel@tonic-gate 		result = REP_FILES;
63*0Sstevel@tonic-gate 	else if (strcmp(rep_name, "nis") == 0)
64*0Sstevel@tonic-gate 		result = REP_NIS;
65*0Sstevel@tonic-gate 	else if (strcmp(rep_name, "nisplus") == 0)
66*0Sstevel@tonic-gate 		result = REP_NISPLUS;
67*0Sstevel@tonic-gate 	else if (strcmp(rep_name, "ldap") == 0)
68*0Sstevel@tonic-gate 		result = REP_LDAP;
69*0Sstevel@tonic-gate 	else if (strcmp(rep_name, "compat") == 0) {
70*0Sstevel@tonic-gate 		struct __nsw_switchconfig *cfg;
71*0Sstevel@tonic-gate 		enum   __nsw_parse_err pserr;
72*0Sstevel@tonic-gate 
73*0Sstevel@tonic-gate 		cfg = __nsw_getconfig("passwd_compat", &pserr);
74*0Sstevel@tonic-gate 		if (cfg == NULL) {
75*0Sstevel@tonic-gate 			result = REP_FILES | REP_NIS;
76*0Sstevel@tonic-gate 		} else {
77*0Sstevel@tonic-gate 			if (strcmp(cfg->lookups->service_name, "nisplus") == 0)
78*0Sstevel@tonic-gate 				result = REP_FILES | REP_NISPLUS;
79*0Sstevel@tonic-gate 			else if (strcmp(cfg->lookups->service_name, "ldap") ==
80*0Sstevel@tonic-gate 			    0)
81*0Sstevel@tonic-gate 				result = REP_FILES | REP_LDAP;
82*0Sstevel@tonic-gate 			else
83*0Sstevel@tonic-gate 				result = REP_ERANGE;
84*0Sstevel@tonic-gate 			__nsw_freeconfig(cfg);
85*0Sstevel@tonic-gate 		}
86*0Sstevel@tonic-gate 	}
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate 	return (result);
89*0Sstevel@tonic-gate }
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate /*
92*0Sstevel@tonic-gate  * Figure out which repository we use in compat mode.
93*0Sstevel@tonic-gate  */
94*0Sstevel@tonic-gate int
95*0Sstevel@tonic-gate get_compat_mode(void)
96*0Sstevel@tonic-gate {
97*0Sstevel@tonic-gate 	struct __nsw_switchconfig *cfg;
98*0Sstevel@tonic-gate 	enum   __nsw_parse_err pserr;
99*0Sstevel@tonic-gate 	int result = REP_COMPAT_NIS;
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate 	if ((cfg = __nsw_getconfig("passwd_compat", &pserr)) != NULL) {
102*0Sstevel@tonic-gate 		if (strcmp(cfg->lookups->service_name, "nisplus") == 0)
103*0Sstevel@tonic-gate 			result = REP_COMPAT_NISPLUS;
104*0Sstevel@tonic-gate 		else if (strcmp(cfg->lookups->service_name, "ldap") == 0)
105*0Sstevel@tonic-gate 			result = REP_COMPAT_LDAP;
106*0Sstevel@tonic-gate 	}
107*0Sstevel@tonic-gate 	__nsw_freeconfig(cfg);
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate 	return (result);
110*0Sstevel@tonic-gate }
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate /*
113*0Sstevel@tonic-gate  * get_ns(rep, accesstype)
114*0Sstevel@tonic-gate  *
115*0Sstevel@tonic-gate  * returns a bitmask of repositories to use based on either
116*0Sstevel@tonic-gate  *   1. the repository that is given as argument
117*0Sstevel@tonic-gate  *   2. the nsswitch.conf file
118*0Sstevel@tonic-gate  *   3. the type of access requested
119*0Sstevel@tonic-gate  *
120*0Sstevel@tonic-gate  * "accesstype" indicates whether we are reading from or writing to the
121*0Sstevel@tonic-gate  * repository. We need to know this since "compat" will translate into
122*0Sstevel@tonic-gate  * REP_NSS (the nss-switch) for READ access (needed to decode
123*0Sstevel@tonic-gate  * the black-magic '+' entries) but it translates into a bitmask
124*0Sstevel@tonic-gate  * on WRITE access.
125*0Sstevel@tonic-gate  *
126*0Sstevel@tonic-gate  * If we detect read-access in compat mode, we augment the result
127*0Sstevel@tonic-gate  * with one of REP_COMPAT_{NIS,NISPLUS,LDAP}. We need this in order to
128*0Sstevel@tonic-gate  * implement ATTR_REP_NAME in nss_getpwnam.
129*0Sstevel@tonic-gate  *
130*0Sstevel@tonic-gate  * A return value of REP_NOREP indicates an error.
131*0Sstevel@tonic-gate  */
132*0Sstevel@tonic-gate int
133*0Sstevel@tonic-gate get_ns(pwu_repository_t *rep, int accesstype)
134*0Sstevel@tonic-gate {
135*0Sstevel@tonic-gate 	struct __nsw_switchconfig *conf = NULL;
136*0Sstevel@tonic-gate 	enum __nsw_parse_err pserr;
137*0Sstevel@tonic-gate 	struct __nsw_lookup *lkp;
138*0Sstevel@tonic-gate 	struct __nsw_lookup *lkp2;
139*0Sstevel@tonic-gate 	int result = REP_NOREP;
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate 	if (rep != PWU_DEFAULT_REP) {
142*0Sstevel@tonic-gate 		result = name_to_int(rep->type);
143*0Sstevel@tonic-gate 		return (result);
144*0Sstevel@tonic-gate 	}
145*0Sstevel@tonic-gate 
146*0Sstevel@tonic-gate 	conf = __nsw_getconfig("passwd", &pserr);
147*0Sstevel@tonic-gate 	if (conf == NULL) {
148*0Sstevel@tonic-gate 		/*
149*0Sstevel@tonic-gate 		 * No config found. The user didn't supply a repository,
150*0Sstevel@tonic-gate 		 * so we try to change the password in the default
151*0Sstevel@tonic-gate 		 * repositories (files and nis) even though we cannot
152*0Sstevel@tonic-gate 		 * find the name service switch entry. (Backward compat)
153*0Sstevel@tonic-gate 		 */
154*0Sstevel@tonic-gate 		syslog(LOG_ERR, "passwdutil.so: nameservice switch entry for "
155*0Sstevel@tonic-gate 				"passwd not found.");
156*0Sstevel@tonic-gate 		result = REP_FILES | REP_NIS;
157*0Sstevel@tonic-gate 		return (result);
158*0Sstevel@tonic-gate 	}
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate 	lkp = conf->lookups;
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate 	/*
163*0Sstevel@tonic-gate 	 * Supported nsswitch.conf can have a maximum of 2 repositories.
164*0Sstevel@tonic-gate 	 * If we encounter an unsupported nsswitch.conf, we return REP_NSS
165*0Sstevel@tonic-gate 	 * to fall back to the nsswitch backend.
166*0Sstevel@tonic-gate 	 */
167*0Sstevel@tonic-gate 	if (conf->num_lookups == 1) {
168*0Sstevel@tonic-gate 		/* files or compat */
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 		if (strcmp(lkp->service_name, "files") == 0) {
171*0Sstevel@tonic-gate 			result = name_to_int(lkp->service_name);
172*0Sstevel@tonic-gate 		} else if (strcmp(lkp->service_name, "compat") == 0) {
173*0Sstevel@tonic-gate 			if (accesstype == PWU_READ)
174*0Sstevel@tonic-gate 				result = REP_NSS | get_compat_mode();
175*0Sstevel@tonic-gate 			else
176*0Sstevel@tonic-gate 				result = name_to_int(lkp->service_name);
177*0Sstevel@tonic-gate 		} else
178*0Sstevel@tonic-gate 			result = REP_NSS;
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate 	} else if (conf->num_lookups == 2) {
181*0Sstevel@tonic-gate 		lkp2 = lkp->next;
182*0Sstevel@tonic-gate 		if (strcmp(lkp->service_name, "files") == 0) {
183*0Sstevel@tonic-gate 			result = REP_FILES;
184*0Sstevel@tonic-gate 			if (strcmp(lkp2->service_name, "ldap") == 0)
185*0Sstevel@tonic-gate 				result |= REP_LDAP;
186*0Sstevel@tonic-gate 			else if (strcmp(lkp2->service_name, "nis") == 0)
187*0Sstevel@tonic-gate 				result |= REP_NIS;
188*0Sstevel@tonic-gate 			else if (strcmp(lkp2->service_name, "nisplus") == 0)
189*0Sstevel@tonic-gate 				result |= REP_NISPLUS;
190*0Sstevel@tonic-gate 			else
191*0Sstevel@tonic-gate 				result = REP_NSS;
192*0Sstevel@tonic-gate 		} else {
193*0Sstevel@tonic-gate 			result = REP_NSS;
194*0Sstevel@tonic-gate 		}
195*0Sstevel@tonic-gate 	} else {
196*0Sstevel@tonic-gate 		result = REP_NSS;
197*0Sstevel@tonic-gate 	}
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate 	__nsw_freeconfig(conf);
200*0Sstevel@tonic-gate 	return (result);
201*0Sstevel@tonic-gate }
202*0Sstevel@tonic-gate 
203*0Sstevel@tonic-gate static void
204*0Sstevel@tonic-gate nss_ldap_passwd(p)
205*0Sstevel@tonic-gate 	nss_db_params_t	*p;
206*0Sstevel@tonic-gate {
207*0Sstevel@tonic-gate 	p->name = NSS_DBNAM_PASSWD;
208*0Sstevel@tonic-gate 	p->flags |= NSS_USE_DEFAULT_CONFIG;
209*0Sstevel@tonic-gate 	p->default_config = "ldap";
210*0Sstevel@tonic-gate }
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate static void
213*0Sstevel@tonic-gate nss_ldap_shadow(p)
214*0Sstevel@tonic-gate 	nss_db_params_t	*p;
215*0Sstevel@tonic-gate {
216*0Sstevel@tonic-gate 	p->name = NSS_DBNAM_SHADOW;
217*0Sstevel@tonic-gate 	p->config_name    = NSS_DBNAM_PASSWD;	/* Use config for "passwd" */
218*0Sstevel@tonic-gate 	p->flags |= NSS_USE_DEFAULT_CONFIG;
219*0Sstevel@tonic-gate 	p->default_config = "ldap";
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate #ifdef PAM_NIS
224*0Sstevel@tonic-gate static void
225*0Sstevel@tonic-gate nss_nis_passwd(p)
226*0Sstevel@tonic-gate 	nss_db_params_t	*p;
227*0Sstevel@tonic-gate {
228*0Sstevel@tonic-gate 	p->name = NSS_DBNAM_PASSWD;
229*0Sstevel@tonic-gate 	p->flags |= NSS_USE_DEFAULT_CONFIG;
230*0Sstevel@tonic-gate 	p->default_config = "nis";
231*0Sstevel@tonic-gate }
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate static void
234*0Sstevel@tonic-gate nss_nis_shadow(p)
235*0Sstevel@tonic-gate 	nss_db_params_t	*p;
236*0Sstevel@tonic-gate {
237*0Sstevel@tonic-gate 	p->name = NSS_DBNAM_SHADOW;
238*0Sstevel@tonic-gate 	p->config_name    = NSS_DBNAM_PASSWD;	/* Use config for "passwd" */
239*0Sstevel@tonic-gate 	p->flags |= NSS_USE_DEFAULT_CONFIG;
240*0Sstevel@tonic-gate 	p->default_config = "nis";
241*0Sstevel@tonic-gate }
242*0Sstevel@tonic-gate #endif /* PAM_NIS */
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate static void
246*0Sstevel@tonic-gate nss_nisplus_passwd(p)
247*0Sstevel@tonic-gate 	nss_db_params_t	*p;
248*0Sstevel@tonic-gate {
249*0Sstevel@tonic-gate 	p->name = NSS_DBNAM_PASSWD;
250*0Sstevel@tonic-gate 	p->flags |= NSS_USE_DEFAULT_CONFIG;
251*0Sstevel@tonic-gate 	p->default_config = "nisplus";
252*0Sstevel@tonic-gate }
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate static void
255*0Sstevel@tonic-gate nss_nisplus_shadow(p)
256*0Sstevel@tonic-gate 	nss_db_params_t	*p;
257*0Sstevel@tonic-gate {
258*0Sstevel@tonic-gate 	p->name = NSS_DBNAM_SHADOW;
259*0Sstevel@tonic-gate 	p->config_name    = NSS_DBNAM_PASSWD;	/* Use config for "passwd" */
260*0Sstevel@tonic-gate 	p->flags |= NSS_USE_DEFAULT_CONFIG;
261*0Sstevel@tonic-gate 	p->default_config = "nisplus";
262*0Sstevel@tonic-gate }
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate 
265*0Sstevel@tonic-gate static char *
266*0Sstevel@tonic-gate gettok(nextpp)
267*0Sstevel@tonic-gate 	char	**nextpp;
268*0Sstevel@tonic-gate {
269*0Sstevel@tonic-gate 	char	*p = *nextpp;
270*0Sstevel@tonic-gate 	char	*q = p;
271*0Sstevel@tonic-gate 	char	c;
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 	if (p == 0) {
274*0Sstevel@tonic-gate 		return (0);
275*0Sstevel@tonic-gate 	}
276*0Sstevel@tonic-gate 	while ((c = *q) != '\0' && c != ':') {
277*0Sstevel@tonic-gate 		q++;
278*0Sstevel@tonic-gate 	}
279*0Sstevel@tonic-gate 	if (c == '\0') {
280*0Sstevel@tonic-gate 		*nextpp = 0;
281*0Sstevel@tonic-gate 	} else {
282*0Sstevel@tonic-gate 		*q++ = '\0';
283*0Sstevel@tonic-gate 		*nextpp = q;
284*0Sstevel@tonic-gate 	}
285*0Sstevel@tonic-gate 	return (p);
286*0Sstevel@tonic-gate }
287*0Sstevel@tonic-gate 
288*0Sstevel@tonic-gate /*
289*0Sstevel@tonic-gate  * Return values: 0 = success, 1 = parse error, 2 = erange ...
290*0Sstevel@tonic-gate  * The structure pointer passed in is a structure in the caller's space
291*0Sstevel@tonic-gate  * wherein the field pointers would be set to areas in the buffer if
292*0Sstevel@tonic-gate  * need be. instring and buffer should be separate areas.
293*0Sstevel@tonic-gate  */
294*0Sstevel@tonic-gate static int
295*0Sstevel@tonic-gate str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
296*0Sstevel@tonic-gate {
297*0Sstevel@tonic-gate 	struct passwd	*passwd	= (struct passwd *)ent;
298*0Sstevel@tonic-gate 	char		*p, *next;
299*0Sstevel@tonic-gate 	int		black_magic;	/* "+" or "-" entry */
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate 	if (lenstr + 1 > buflen) {
302*0Sstevel@tonic-gate 		return (NSS_STR_PARSE_ERANGE);
303*0Sstevel@tonic-gate 	}
304*0Sstevel@tonic-gate 	/*
305*0Sstevel@tonic-gate 	 * We copy the input string into the output buffer and
306*0Sstevel@tonic-gate 	 * operate on it in place.
307*0Sstevel@tonic-gate 	 */
308*0Sstevel@tonic-gate 	(void) memcpy(buffer, instr, lenstr);
309*0Sstevel@tonic-gate 	buffer[lenstr] = '\0';
310*0Sstevel@tonic-gate 
311*0Sstevel@tonic-gate 	next = buffer;
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 	passwd->pw_name = p = gettok(&next);		/* username */
314*0Sstevel@tonic-gate 	if (*p == '\0') {
315*0Sstevel@tonic-gate 		/* Empty username;  not allowed */
316*0Sstevel@tonic-gate 		return (NSS_STR_PARSE_PARSE);
317*0Sstevel@tonic-gate 	}
318*0Sstevel@tonic-gate 	black_magic = (*p == '+' || *p == '-');
319*0Sstevel@tonic-gate 	if (black_magic) {
320*0Sstevel@tonic-gate 		passwd->pw_uid	= UID_NOBODY;
321*0Sstevel@tonic-gate 		passwd->pw_gid	= GID_NOBODY;
322*0Sstevel@tonic-gate 		/*
323*0Sstevel@tonic-gate 		 * pwconv tests pw_passwd and pw_age == NULL
324*0Sstevel@tonic-gate 		 */
325*0Sstevel@tonic-gate 		passwd->pw_passwd = "";
326*0Sstevel@tonic-gate 		passwd->pw_age	= "";
327*0Sstevel@tonic-gate 		/*
328*0Sstevel@tonic-gate 		 * the rest of the passwd entry is "optional"
329*0Sstevel@tonic-gate 		 */
330*0Sstevel@tonic-gate 		passwd->pw_comment = "";
331*0Sstevel@tonic-gate 		passwd->pw_gecos = "";
332*0Sstevel@tonic-gate 		passwd->pw_dir	= "";
333*0Sstevel@tonic-gate 		passwd->pw_shell = "";
334*0Sstevel@tonic-gate 	}
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	passwd->pw_passwd = p = gettok(&next);		/* password */
337*0Sstevel@tonic-gate 	if (p == 0) {
338*0Sstevel@tonic-gate 		if (black_magic)
339*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
340*0Sstevel@tonic-gate 		else
341*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
342*0Sstevel@tonic-gate 	}
343*0Sstevel@tonic-gate 	for (; *p != '\0'; p++) {			/* age */
344*0Sstevel@tonic-gate 		if (*p == ',') {
345*0Sstevel@tonic-gate 			*p++ = '\0';
346*0Sstevel@tonic-gate 			break;
347*0Sstevel@tonic-gate 		}
348*0Sstevel@tonic-gate 	}
349*0Sstevel@tonic-gate 	passwd->pw_age = p;
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate 	p = next;					/* uid */
352*0Sstevel@tonic-gate 	if (p == 0 || *p == '\0') {
353*0Sstevel@tonic-gate 		if (black_magic)
354*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
355*0Sstevel@tonic-gate 		else
356*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
357*0Sstevel@tonic-gate 	}
358*0Sstevel@tonic-gate 	if (!black_magic) {
359*0Sstevel@tonic-gate 		passwd->pw_uid = strtol(p, &next, 10);
360*0Sstevel@tonic-gate 		if (next == p) {
361*0Sstevel@tonic-gate 			/* uid field should be nonempty */
362*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
363*0Sstevel@tonic-gate 		}
364*0Sstevel@tonic-gate 		/*
365*0Sstevel@tonic-gate 		 * The old code (in 2.0 thru 2.5) would check
366*0Sstevel@tonic-gate 		 * for the uid being negative, or being greater
367*0Sstevel@tonic-gate 		 * than 60001 (the rfs limit).  If it met either of
368*0Sstevel@tonic-gate 		 * these conditions, the uid was translated to 60001.
369*0Sstevel@tonic-gate 		 *
370*0Sstevel@tonic-gate 		 * Now we just check for negative uids; anything else
371*0Sstevel@tonic-gate 		 * is administrative policy
372*0Sstevel@tonic-gate 		 */
373*0Sstevel@tonic-gate 		if (passwd->pw_uid < 0)
374*0Sstevel@tonic-gate 			passwd->pw_uid = UID_NOBODY;
375*0Sstevel@tonic-gate 	}
376*0Sstevel@tonic-gate 	if (*next++ != ':') {
377*0Sstevel@tonic-gate 		if (black_magic)
378*0Sstevel@tonic-gate 			p = gettok(&next);
379*0Sstevel@tonic-gate 		else
380*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
381*0Sstevel@tonic-gate 	}
382*0Sstevel@tonic-gate 	p = next;					/* gid */
383*0Sstevel@tonic-gate 	if (p == 0 || *p == '\0') {
384*0Sstevel@tonic-gate 		if (black_magic)
385*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
386*0Sstevel@tonic-gate 		else
387*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
388*0Sstevel@tonic-gate 	}
389*0Sstevel@tonic-gate 	if (!black_magic) {
390*0Sstevel@tonic-gate 		passwd->pw_gid = strtol(p, &next, 10);
391*0Sstevel@tonic-gate 		if (next == p) {
392*0Sstevel@tonic-gate 			/* gid field should be nonempty */
393*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
394*0Sstevel@tonic-gate 		}
395*0Sstevel@tonic-gate 		/*
396*0Sstevel@tonic-gate 		 * gid should be non-negative; anything else
397*0Sstevel@tonic-gate 		 * is administrative policy.
398*0Sstevel@tonic-gate 		 */
399*0Sstevel@tonic-gate 		if (passwd->pw_gid < 0)
400*0Sstevel@tonic-gate 			passwd->pw_gid = GID_NOBODY;
401*0Sstevel@tonic-gate 	}
402*0Sstevel@tonic-gate 	if (*next++ != ':') {
403*0Sstevel@tonic-gate 		if (black_magic)
404*0Sstevel@tonic-gate 			p = gettok(&next);
405*0Sstevel@tonic-gate 		else
406*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
407*0Sstevel@tonic-gate 	}
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 	passwd->pw_gecos = passwd->pw_comment = p = gettok(&next);
410*0Sstevel@tonic-gate 	if (p == 0) {
411*0Sstevel@tonic-gate 		if (black_magic)
412*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
413*0Sstevel@tonic-gate 		else
414*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
415*0Sstevel@tonic-gate 	}
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 	passwd->pw_dir = p = gettok(&next);
418*0Sstevel@tonic-gate 	if (p == 0) {
419*0Sstevel@tonic-gate 		if (black_magic)
420*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
421*0Sstevel@tonic-gate 		else
422*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
423*0Sstevel@tonic-gate 	}
424*0Sstevel@tonic-gate 
425*0Sstevel@tonic-gate 	passwd->pw_shell = p = gettok(&next);
426*0Sstevel@tonic-gate 	if (p == 0) {
427*0Sstevel@tonic-gate 		if (black_magic)
428*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
429*0Sstevel@tonic-gate 		else
430*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
431*0Sstevel@tonic-gate 	}
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	/* Better not be any more fields... */
434*0Sstevel@tonic-gate 	if (next == 0) {
435*0Sstevel@tonic-gate 		/* Successfully parsed and stored */
436*0Sstevel@tonic-gate 		return (NSS_STR_PARSE_SUCCESS);
437*0Sstevel@tonic-gate 	}
438*0Sstevel@tonic-gate 	return (NSS_STR_PARSE_PARSE);
439*0Sstevel@tonic-gate }
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate typedef const char *constp;
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate /*
444*0Sstevel@tonic-gate  * Return value 1 means success and more input, 0 means error or no more
445*0Sstevel@tonic-gate  */
446*0Sstevel@tonic-gate static int
447*0Sstevel@tonic-gate getfield(nextp, limit, uns, valp)
448*0Sstevel@tonic-gate 	constp		*nextp;
449*0Sstevel@tonic-gate 	constp		limit;
450*0Sstevel@tonic-gate 	int		uns;
451*0Sstevel@tonic-gate 	void		*valp;
452*0Sstevel@tonic-gate {
453*0Sstevel@tonic-gate 	constp		p = *nextp;
454*0Sstevel@tonic-gate 	char		*endfield;
455*0Sstevel@tonic-gate 	char		numbuf[12];  /* Holds -2^31 and trailing ':' */
456*0Sstevel@tonic-gate 	int		len;
457*0Sstevel@tonic-gate 	long		x;
458*0Sstevel@tonic-gate 	unsigned long	ux;
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 	if (p == 0 || p >= limit) {
461*0Sstevel@tonic-gate 		return (0);
462*0Sstevel@tonic-gate 	}
463*0Sstevel@tonic-gate 	if (*p == ':') {
464*0Sstevel@tonic-gate 		p++;
465*0Sstevel@tonic-gate 		*nextp = p;
466*0Sstevel@tonic-gate 		return (p < limit);
467*0Sstevel@tonic-gate 	}
468*0Sstevel@tonic-gate 	if ((len = limit - p) > sizeof (numbuf) - 1) {
469*0Sstevel@tonic-gate 		len = sizeof (numbuf) - 1;
470*0Sstevel@tonic-gate 	}
471*0Sstevel@tonic-gate 	/*
472*0Sstevel@tonic-gate 	 * We want to use strtol() and we have a readonly non-zero-terminated
473*0Sstevel@tonic-gate 	 *   string, so first we copy and terminate the interesting bit.
474*0Sstevel@tonic-gate 	 *   Ugh.  (It's convenient to terminate with a colon rather than \0).
475*0Sstevel@tonic-gate 	 */
476*0Sstevel@tonic-gate 	if ((endfield = memccpy(numbuf, p, ':', len)) == 0) {
477*0Sstevel@tonic-gate 		if (len != limit - p) {
478*0Sstevel@tonic-gate 			/* Error -- field is too big to be a legit number */
479*0Sstevel@tonic-gate 			return (0);
480*0Sstevel@tonic-gate 		}
481*0Sstevel@tonic-gate 		numbuf[len] = ':';
482*0Sstevel@tonic-gate 		p = limit;
483*0Sstevel@tonic-gate 	} else {
484*0Sstevel@tonic-gate 		p += (endfield - numbuf);
485*0Sstevel@tonic-gate 	}
486*0Sstevel@tonic-gate 	if (uns) {
487*0Sstevel@tonic-gate 		ux = strtoul(numbuf, &endfield, 10);
488*0Sstevel@tonic-gate 		if (*endfield != ':') {
489*0Sstevel@tonic-gate 			/* Error -- expected <integer><colon> */
490*0Sstevel@tonic-gate 			return (0);
491*0Sstevel@tonic-gate 		}
492*0Sstevel@tonic-gate 		*((unsigned int *)valp) = (unsigned int)ux;
493*0Sstevel@tonic-gate 	} else {
494*0Sstevel@tonic-gate 		x = strtol(numbuf, &endfield, 10);
495*0Sstevel@tonic-gate 		if (*endfield != ':') {
496*0Sstevel@tonic-gate 			/* Error -- expected <integer><colon> */
497*0Sstevel@tonic-gate 			return (0);
498*0Sstevel@tonic-gate 		}
499*0Sstevel@tonic-gate 		*((int *)valp) = (int)x;
500*0Sstevel@tonic-gate 	}
501*0Sstevel@tonic-gate 	*nextp = p;
502*0Sstevel@tonic-gate 	return (p < limit);
503*0Sstevel@tonic-gate }
504*0Sstevel@tonic-gate 
505*0Sstevel@tonic-gate /*
506*0Sstevel@tonic-gate  *  str2spwd() -- convert a string to a shadow passwd entry.  The parser is
507*0Sstevel@tonic-gate  *	more liberal than the passwd or group parsers;  since it's legitimate
508*0Sstevel@tonic-gate  *	for almost all the fields here to be blank, the parser lets one omit
509*0Sstevel@tonic-gate  *	any number of blank fields at the end of the entry.  The acceptable
510*0Sstevel@tonic-gate  *	forms for '+' and '-' entries are the same as those for normal entries.
511*0Sstevel@tonic-gate  *  === Is this likely to do more harm than good?
512*0Sstevel@tonic-gate  *
513*0Sstevel@tonic-gate  * Return values: 0 = success, 1 = parse error, 2 = erange ...
514*0Sstevel@tonic-gate  * The structure pointer passed in is a structure in the caller's space
515*0Sstevel@tonic-gate  * wherein the field pointers would be set to areas in the buffer if
516*0Sstevel@tonic-gate  * need be. instring and buffer should be separate areas.
517*0Sstevel@tonic-gate  */
518*0Sstevel@tonic-gate int
519*0Sstevel@tonic-gate str2spwd(instr, lenstr, ent, buffer, buflen)
520*0Sstevel@tonic-gate 	const char	*instr;
521*0Sstevel@tonic-gate 	int		lenstr;
522*0Sstevel@tonic-gate 	void	*ent; /* really (struct spwd *) */
523*0Sstevel@tonic-gate 	char	*buffer;
524*0Sstevel@tonic-gate 	int	buflen;
525*0Sstevel@tonic-gate {
526*0Sstevel@tonic-gate 	struct spwd	*shadow	= (struct spwd *)ent;
527*0Sstevel@tonic-gate 	const char	*p = instr, *limit;
528*0Sstevel@tonic-gate 	char		*bufp;
529*0Sstevel@tonic-gate 	int	lencopy, black_magic;
530*0Sstevel@tonic-gate 
531*0Sstevel@tonic-gate 	limit = p + lenstr;
532*0Sstevel@tonic-gate 	if ((p = memchr(instr, ':', lenstr)) == 0 ||
533*0Sstevel@tonic-gate 		++p >= limit ||
534*0Sstevel@tonic-gate 		(p = memchr(p, ':', limit - p)) == 0) {
535*0Sstevel@tonic-gate 		lencopy = lenstr;
536*0Sstevel@tonic-gate 		p = 0;
537*0Sstevel@tonic-gate 	} else {
538*0Sstevel@tonic-gate 		lencopy = p - instr;
539*0Sstevel@tonic-gate 		p++;
540*0Sstevel@tonic-gate 	}
541*0Sstevel@tonic-gate 	if (lencopy + 1 > buflen) {
542*0Sstevel@tonic-gate 		return (NSS_STR_PARSE_ERANGE);
543*0Sstevel@tonic-gate 	}
544*0Sstevel@tonic-gate 	(void) memcpy(buffer, instr, lencopy);
545*0Sstevel@tonic-gate 	buffer[lencopy] = 0;
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 	black_magic = (*instr == '+' || *instr == '-');
548*0Sstevel@tonic-gate 	shadow->sp_namp = bufp = buffer;
549*0Sstevel@tonic-gate 	shadow->sp_pwdp	= 0;
550*0Sstevel@tonic-gate 	shadow->sp_lstchg = -1;
551*0Sstevel@tonic-gate 	shadow->sp_min	= -1;
552*0Sstevel@tonic-gate 	shadow->sp_max	= -1;
553*0Sstevel@tonic-gate 	shadow->sp_warn	= -1;
554*0Sstevel@tonic-gate 	shadow->sp_inact = -1;
555*0Sstevel@tonic-gate 	shadow->sp_expire = -1;
556*0Sstevel@tonic-gate 	shadow->sp_flag	= 0;
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	if ((bufp = strchr(bufp, ':')) == 0) {
559*0Sstevel@tonic-gate 		if (black_magic)
560*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
561*0Sstevel@tonic-gate 		else
562*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_PARSE);
563*0Sstevel@tonic-gate 	}
564*0Sstevel@tonic-gate 	*bufp++ = '\0';
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate 	shadow->sp_pwdp = bufp;
567*0Sstevel@tonic-gate 	if (instr == 0) {
568*0Sstevel@tonic-gate 		if ((bufp = strchr(bufp, ':')) == 0) {
569*0Sstevel@tonic-gate 			if (black_magic)
570*0Sstevel@tonic-gate 				return (NSS_STR_PARSE_SUCCESS);
571*0Sstevel@tonic-gate 			else
572*0Sstevel@tonic-gate 				return (NSS_STR_PARSE_PARSE);
573*0Sstevel@tonic-gate 		}
574*0Sstevel@tonic-gate 		*bufp++ = '\0';
575*0Sstevel@tonic-gate 		p = bufp;
576*0Sstevel@tonic-gate 	} /* else p was set when we copied name and passwd into the buffer */
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &shadow->sp_lstchg))
579*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
580*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &shadow->sp_min))
581*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
582*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &shadow->sp_max))
583*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
584*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &shadow->sp_warn))
585*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
586*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &shadow->sp_inact))
587*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
588*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &shadow->sp_expire))
589*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
590*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 1, &shadow->sp_flag))
591*0Sstevel@tonic-gate 			return (NSS_STR_PARSE_SUCCESS);
592*0Sstevel@tonic-gate 	if (p != limit) {
593*0Sstevel@tonic-gate 		/* Syntax error -- garbage at end of line */
594*0Sstevel@tonic-gate 		return (NSS_STR_PARSE_PARSE);
595*0Sstevel@tonic-gate 	}
596*0Sstevel@tonic-gate 	return (NSS_STR_PARSE_SUCCESS);
597*0Sstevel@tonic-gate }
598*0Sstevel@tonic-gate 
599*0Sstevel@tonic-gate static nss_XbyY_buf_t *buffer;
600*0Sstevel@tonic-gate static DEFINE_NSS_DB_ROOT(db_root);
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate #define	GETBUF()	\
603*0Sstevel@tonic-gate 	NSS_XbyY_ALLOC(&buffer, sizeof (struct passwd), NSS_BUFLEN_PASSWD)
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate #pragma fini(endutilpwent)
606*0Sstevel@tonic-gate 
607*0Sstevel@tonic-gate static void
608*0Sstevel@tonic-gate endutilpwent(void)
609*0Sstevel@tonic-gate {
610*0Sstevel@tonic-gate 	NSS_XbyY_FREE(&buffer);
611*0Sstevel@tonic-gate 	nss_delete(&db_root);
612*0Sstevel@tonic-gate }
613*0Sstevel@tonic-gate 
614*0Sstevel@tonic-gate struct passwd *
615*0Sstevel@tonic-gate getpwnam_from(const char *name, pwu_repository_t *rep, int reptype)
616*0Sstevel@tonic-gate {
617*0Sstevel@tonic-gate 	nss_XbyY_buf_t  *b = GETBUF();
618*0Sstevel@tonic-gate 	nss_XbyY_args_t arg;
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 	if (b == 0)
621*0Sstevel@tonic-gate 		return (0);
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 	NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd);
624*0Sstevel@tonic-gate 	arg.key.name = name;
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 	switch (reptype) {
627*0Sstevel@tonic-gate 	case REP_LDAP:
628*0Sstevel@tonic-gate 		(void) nss_search(&db_root, nss_ldap_passwd,
629*0Sstevel@tonic-gate 		    NSS_DBOP_PASSWD_BYNAME, &arg);
630*0Sstevel@tonic-gate 		break;
631*0Sstevel@tonic-gate 	case REP_NISPLUS:
632*0Sstevel@tonic-gate 		if (rep && rep->scope)
633*0Sstevel@tonic-gate 			return (nisplus_getpw_from_master(name, rep->scope));
634*0Sstevel@tonic-gate 
635*0Sstevel@tonic-gate 		(void) nss_search(&db_root, nss_nisplus_passwd,
636*0Sstevel@tonic-gate 		    NSS_DBOP_PASSWD_BYNAME, &arg);
637*0Sstevel@tonic-gate 		break;
638*0Sstevel@tonic-gate #ifdef PAM_NIS
639*0Sstevel@tonic-gate 	case REP_NIS:
640*0Sstevel@tonic-gate 		(void) nss_search(&db_root, nss_nis_passwd,
641*0Sstevel@tonic-gate 		    NSS_DBOP_PASSWD_BYNAME, &arg);
642*0Sstevel@tonic-gate 		break;
643*0Sstevel@tonic-gate #endif
644*0Sstevel@tonic-gate 	default:
645*0Sstevel@tonic-gate 		return (NULL);
646*0Sstevel@tonic-gate 	}
647*0Sstevel@tonic-gate 
648*0Sstevel@tonic-gate 	return (struct passwd *)NSS_XbyY_FINI(&arg);
649*0Sstevel@tonic-gate }
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate /*ARGSUSED*/
652*0Sstevel@tonic-gate struct passwd *
653*0Sstevel@tonic-gate getpwuid_from(uid_t uid, pwu_repository_t *rep, int reptype)
654*0Sstevel@tonic-gate {
655*0Sstevel@tonic-gate 	nss_XbyY_buf_t  *b = GETBUF();
656*0Sstevel@tonic-gate 	nss_XbyY_args_t arg;
657*0Sstevel@tonic-gate 
658*0Sstevel@tonic-gate 	if (b == 0)
659*0Sstevel@tonic-gate 		return (0);
660*0Sstevel@tonic-gate 
661*0Sstevel@tonic-gate 	NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2passwd);
662*0Sstevel@tonic-gate 	arg.key.uid = uid;
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 	switch (reptype) {
665*0Sstevel@tonic-gate 	case REP_LDAP:
666*0Sstevel@tonic-gate 		(void) nss_search(&db_root, nss_ldap_passwd,
667*0Sstevel@tonic-gate 		    NSS_DBOP_PASSWD_BYUID, &arg);
668*0Sstevel@tonic-gate 		break;
669*0Sstevel@tonic-gate 	case REP_NISPLUS:
670*0Sstevel@tonic-gate 		(void) nss_search(&db_root, nss_nisplus_passwd,
671*0Sstevel@tonic-gate 		    NSS_DBOP_PASSWD_BYUID, &arg);
672*0Sstevel@tonic-gate 		break;
673*0Sstevel@tonic-gate #ifdef PAM_NIS
674*0Sstevel@tonic-gate 	case REP_NIS:
675*0Sstevel@tonic-gate 		(void) nss_search(&db_root, nss_nis_passwd,
676*0Sstevel@tonic-gate 		    NSS_DBOP_PASSWD_BYUID, &arg);
677*0Sstevel@tonic-gate 		break;
678*0Sstevel@tonic-gate #endif
679*0Sstevel@tonic-gate 	default:
680*0Sstevel@tonic-gate 		return (NULL);
681*0Sstevel@tonic-gate 	}
682*0Sstevel@tonic-gate 
683*0Sstevel@tonic-gate 	return (struct passwd *)NSS_XbyY_FINI(&arg);
684*0Sstevel@tonic-gate }
685*0Sstevel@tonic-gate 
686*0Sstevel@tonic-gate static nss_XbyY_buf_t *spbuf;
687*0Sstevel@tonic-gate static DEFINE_NSS_DB_ROOT(spdb_root);
688*0Sstevel@tonic-gate 
689*0Sstevel@tonic-gate #define	GETSPBUF()	\
690*0Sstevel@tonic-gate 	NSS_XbyY_ALLOC(&spbuf, sizeof (struct spwd), NSS_BUFLEN_SHADOW)
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate #pragma fini(endutilspent)
693*0Sstevel@tonic-gate 
694*0Sstevel@tonic-gate static void
695*0Sstevel@tonic-gate endutilspent(void)
696*0Sstevel@tonic-gate {
697*0Sstevel@tonic-gate 	NSS_XbyY_FREE(&spbuf);
698*0Sstevel@tonic-gate 	nss_delete(&spdb_root);
699*0Sstevel@tonic-gate }
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate struct spwd *
702*0Sstevel@tonic-gate getspnam_from(const char *name, pwu_repository_t *rep, int reptype)
703*0Sstevel@tonic-gate {
704*0Sstevel@tonic-gate 	nss_XbyY_buf_t  *b = GETSPBUF();
705*0Sstevel@tonic-gate 	nss_XbyY_args_t arg;
706*0Sstevel@tonic-gate 
707*0Sstevel@tonic-gate 	if (b == 0)
708*0Sstevel@tonic-gate 		return (0);
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 	NSS_XbyY_INIT(&arg, b->result, b->buffer, b->buflen, str2spwd);
711*0Sstevel@tonic-gate 	arg.key.name = name;
712*0Sstevel@tonic-gate 	switch (reptype) {
713*0Sstevel@tonic-gate 	case REP_LDAP:
714*0Sstevel@tonic-gate 		(void) nss_search(&spdb_root, nss_ldap_shadow,
715*0Sstevel@tonic-gate 		    NSS_DBOP_SHADOW_BYNAME, &arg);
716*0Sstevel@tonic-gate 		break;
717*0Sstevel@tonic-gate 	case REP_NISPLUS:
718*0Sstevel@tonic-gate 		if (rep && rep->scope)
719*0Sstevel@tonic-gate 			return (nisplus_getsp_from_master(name, rep->scope));
720*0Sstevel@tonic-gate 
721*0Sstevel@tonic-gate 		(void) nss_search(&spdb_root, nss_nisplus_shadow,
722*0Sstevel@tonic-gate 		    NSS_DBOP_SHADOW_BYNAME, &arg);
723*0Sstevel@tonic-gate 		break;
724*0Sstevel@tonic-gate #ifdef PAM_NIS
725*0Sstevel@tonic-gate 	case REP_NIS:
726*0Sstevel@tonic-gate 		(void) nss_search(&spdb_root, nss_nis_shadow,
727*0Sstevel@tonic-gate 		    NSS_DBOP_SHADOW_BYNAME, &arg);
728*0Sstevel@tonic-gate 		break;
729*0Sstevel@tonic-gate #endif
730*0Sstevel@tonic-gate 	default:
731*0Sstevel@tonic-gate 		return (NULL);
732*0Sstevel@tonic-gate 	}
733*0Sstevel@tonic-gate 	return (struct spwd *)NSS_XbyY_FINI(&arg);
734*0Sstevel@tonic-gate }
735*0Sstevel@tonic-gate 
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate static nis_result *
738*0Sstevel@tonic-gate nisplus_match(const char *name, char *domain, char *buf, int len)
739*0Sstevel@tonic-gate {
740*0Sstevel@tonic-gate 	int n;
741*0Sstevel@tonic-gate 	int flags;
742*0Sstevel@tonic-gate 	nis_result *res;
743*0Sstevel@tonic-gate 	nis_object *object;
744*0Sstevel@tonic-gate 
745*0Sstevel@tonic-gate 	n = snprintf(buf, len, "[name=%s],passwd.org_dir.%s", name, domain);
746*0Sstevel@tonic-gate 	if (n >= len) {
747*0Sstevel@tonic-gate 		syslog(LOG_ERR, "nisplus_match: name too long");
748*0Sstevel@tonic-gate 		return (NULL);
749*0Sstevel@tonic-gate 	}
750*0Sstevel@tonic-gate 	if (buf[n-1] != '.') {
751*0Sstevel@tonic-gate 		if (n == len-1) {
752*0Sstevel@tonic-gate 			syslog(LOG_ERR, "nisplus_match: name too long");
753*0Sstevel@tonic-gate 			return (NULL);
754*0Sstevel@tonic-gate 		}
755*0Sstevel@tonic-gate 		buf[n++] = '.';
756*0Sstevel@tonic-gate 		buf[n] = '\0';
757*0Sstevel@tonic-gate 	}
758*0Sstevel@tonic-gate 
759*0Sstevel@tonic-gate 	flags = USE_DGRAM | FOLLOW_LINKS | FOLLOW_PATH | MASTER_ONLY;
760*0Sstevel@tonic-gate 
761*0Sstevel@tonic-gate 	res = nis_list(buf, flags, NULL, NULL);
762*0Sstevel@tonic-gate 
763*0Sstevel@tonic-gate 	if (res == NULL) {
764*0Sstevel@tonic-gate 		syslog(LOG_ERR, "nisplus_match: nis_list returned NULL");
765*0Sstevel@tonic-gate 		return (NULL);
766*0Sstevel@tonic-gate 	}
767*0Sstevel@tonic-gate 
768*0Sstevel@tonic-gate 	if (NIS_RES_STATUS(res) != NIS_SUCCESS || NIS_RES_NUMOBJ(res) != 1) {
769*0Sstevel@tonic-gate 		syslog(LOG_ERR, "nisplus_match: match failed: %s",
770*0Sstevel@tonic-gate 		    nis_sperrno(NIS_RES_STATUS(res)));
771*0Sstevel@tonic-gate 		nis_freeresult(res);
772*0Sstevel@tonic-gate 		return (NULL);
773*0Sstevel@tonic-gate 	}
774*0Sstevel@tonic-gate 
775*0Sstevel@tonic-gate 	object = NIS_RES_OBJECT(res);
776*0Sstevel@tonic-gate 
777*0Sstevel@tonic-gate 	if (object->EN_data.en_cols.en_cols_len < 8) {
778*0Sstevel@tonic-gate 		syslog(LOG_ERR, "nisplus_match: "
779*0Sstevel@tonic-gate 		    "not a valid passwd table entry for user %s", name);
780*0Sstevel@tonic-gate 		nis_freeresult(res);
781*0Sstevel@tonic-gate 		return (NULL);
782*0Sstevel@tonic-gate 	}
783*0Sstevel@tonic-gate 
784*0Sstevel@tonic-gate 	return (res);
785*0Sstevel@tonic-gate }
786*0Sstevel@tonic-gate 
787*0Sstevel@tonic-gate #define	SAFE_STRDUP(dst, idx) \
788*0Sstevel@tonic-gate 	if ((idx) <= 3 && ENTRY_VAL(nret, (idx)) == NULL) { \
789*0Sstevel@tonic-gate 		syslog(LOG_ERR, \
790*0Sstevel@tonic-gate 		    "passwdutil: missing field from password entry"); \
791*0Sstevel@tonic-gate 		goto error; \
792*0Sstevel@tonic-gate 	} \
793*0Sstevel@tonic-gate 	len = ENTRY_LEN(nret, (idx)); \
794*0Sstevel@tonic-gate 	(dst) = malloc(len+1); \
795*0Sstevel@tonic-gate 	if ((dst) == NULL) { \
796*0Sstevel@tonic-gate 		syslog(LOG_ERR, "passwdutil: out of memory"); \
797*0Sstevel@tonic-gate 		goto error; \
798*0Sstevel@tonic-gate 	} \
799*0Sstevel@tonic-gate 	(dst)[len] = '\0'; \
800*0Sstevel@tonic-gate 	(void) strncpy((dst), \
801*0Sstevel@tonic-gate 	    ENTRY_VAL(nret, (idx)) ? ENTRY_VAL(nret, (idx)) : "", \
802*0Sstevel@tonic-gate 	    len);
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate 
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate static struct passwd *
807*0Sstevel@tonic-gate nisplus_getpw_from_master(const char *name, char *domain)
808*0Sstevel@tonic-gate {
809*0Sstevel@tonic-gate 	char lookup[NIS_MAXNAMELEN+1];
810*0Sstevel@tonic-gate 	nis_result *res;
811*0Sstevel@tonic-gate 	nis_object *nret;
812*0Sstevel@tonic-gate 	int len;
813*0Sstevel@tonic-gate 	char *p;
814*0Sstevel@tonic-gate 	struct passwd *pw;
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 	if ((pw = calloc(1, sizeof (*pw))) == NULL)
817*0Sstevel@tonic-gate 		return (NULL);
818*0Sstevel@tonic-gate 
819*0Sstevel@tonic-gate 	res = nisplus_match(name, domain, lookup, sizeof (lookup));
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate 	if (res == NULL)
822*0Sstevel@tonic-gate 		return (NULL);
823*0Sstevel@tonic-gate 
824*0Sstevel@tonic-gate 	nret = NIS_RES_OBJECT(res);
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate 	SAFE_STRDUP(pw->pw_name, 0);
827*0Sstevel@tonic-gate 
828*0Sstevel@tonic-gate 	if ((pw->pw_passwd = strdup("x")) == NULL) {
829*0Sstevel@tonic-gate 		syslog(LOG_ERR, "passwdutil: out of memory");
830*0Sstevel@tonic-gate 		goto error;
831*0Sstevel@tonic-gate 	}
832*0Sstevel@tonic-gate 
833*0Sstevel@tonic-gate 	SAFE_STRDUP(p, 2);
834*0Sstevel@tonic-gate 	pw->pw_uid = atoi(p);
835*0Sstevel@tonic-gate 	free(p);
836*0Sstevel@tonic-gate 
837*0Sstevel@tonic-gate 	SAFE_STRDUP(p, 3);
838*0Sstevel@tonic-gate 	pw->pw_gid = atoi(p);
839*0Sstevel@tonic-gate 	free(p);
840*0Sstevel@tonic-gate 
841*0Sstevel@tonic-gate 	pw->pw_age = NULL;
842*0Sstevel@tonic-gate 	pw->pw_comment = NULL;
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate /*CONSTANTCONDITION*/
845*0Sstevel@tonic-gate 	SAFE_STRDUP(pw->pw_gecos, 4);
846*0Sstevel@tonic-gate 
847*0Sstevel@tonic-gate /*CONSTANTCONDITION*/
848*0Sstevel@tonic-gate 	SAFE_STRDUP(pw->pw_dir, 5);
849*0Sstevel@tonic-gate 
850*0Sstevel@tonic-gate /*CONSTANTCONDITION*/
851*0Sstevel@tonic-gate 	SAFE_STRDUP(pw->pw_shell, 6);
852*0Sstevel@tonic-gate 
853*0Sstevel@tonic-gate 	nis_freeresult(res);
854*0Sstevel@tonic-gate 
855*0Sstevel@tonic-gate 	return (pw);
856*0Sstevel@tonic-gate 
857*0Sstevel@tonic-gate error:
858*0Sstevel@tonic-gate 	nis_freeresult(res);
859*0Sstevel@tonic-gate 	if (pw->pw_name)
860*0Sstevel@tonic-gate 		free(pw->pw_name);
861*0Sstevel@tonic-gate 	if (pw->pw_passwd)
862*0Sstevel@tonic-gate 		free(pw->pw_passwd);
863*0Sstevel@tonic-gate 	if (pw->pw_gecos)
864*0Sstevel@tonic-gate 		free(pw->pw_gecos);
865*0Sstevel@tonic-gate 	if (pw->pw_dir)
866*0Sstevel@tonic-gate 		free(pw->pw_dir);
867*0Sstevel@tonic-gate 	if (pw->pw_shell)
868*0Sstevel@tonic-gate 		free(pw->pw_shell);
869*0Sstevel@tonic-gate 	free(pw);
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate 	return (NULL);
872*0Sstevel@tonic-gate }
873*0Sstevel@tonic-gate 
874*0Sstevel@tonic-gate /*
875*0Sstevel@tonic-gate  * struct spwd * nisplus_getsp_from_master()
876*0Sstevel@tonic-gate  *
877*0Sstevel@tonic-gate  * Get the shadow structure from a NIS+ master.
878*0Sstevel@tonic-gate  * This routine normally runs with EUID==0. This can cause trouble
879*0Sstevel@tonic-gate  * if the NIS+ tables are locked down so that only the owner can
880*0Sstevel@tonic-gate  * access the encrypted password. If we detect that scenario, we switch
881*0Sstevel@tonic-gate  * EUID to the owner of the record and refetch it.
882*0Sstevel@tonic-gate  */
883*0Sstevel@tonic-gate static struct spwd *
884*0Sstevel@tonic-gate nisplus_getsp_from_master(const char *name, char *domain)
885*0Sstevel@tonic-gate {
886*0Sstevel@tonic-gate 	char lookup[NIS_MAXNAMELEN+1];
887*0Sstevel@tonic-gate 	nis_result *res = NULL;
888*0Sstevel@tonic-gate 	nis_object *nret = NULL;
889*0Sstevel@tonic-gate 	int len;
890*0Sstevel@tonic-gate 	struct spwd *spw;
891*0Sstevel@tonic-gate 	char *shadow = NULL;
892*0Sstevel@tonic-gate 	const char *p = NULL;
893*0Sstevel@tonic-gate 	const char *limit;
894*0Sstevel@tonic-gate 
895*0Sstevel@tonic-gate 	res = nisplus_match(name, domain, lookup, sizeof (lookup));
896*0Sstevel@tonic-gate 	if (res == NULL)
897*0Sstevel@tonic-gate 		return (NULL);
898*0Sstevel@tonic-gate 
899*0Sstevel@tonic-gate 	nret = NIS_RES_OBJECT(res);
900*0Sstevel@tonic-gate 
901*0Sstevel@tonic-gate 	/*CONSTANTCONDITION*/
902*0Sstevel@tonic-gate 	SAFE_STRDUP(shadow, 7);
903*0Sstevel@tonic-gate 
904*0Sstevel@tonic-gate 	/*
905*0Sstevel@tonic-gate 	 * If we got "*NP*" as password, try again with EUID set to
906*0Sstevel@tonic-gate 	 * the UID of the record-owner.
907*0Sstevel@tonic-gate 	 */
908*0Sstevel@tonic-gate 	if (strncmp(shadow, "*NP*", 4) == 0) {
909*0Sstevel@tonic-gate 		char *p;
910*0Sstevel@tonic-gate 		uid_t owner_uid;
911*0Sstevel@tonic-gate 		uid_t euid = geteuid();
912*0Sstevel@tonic-gate 
913*0Sstevel@tonic-gate 		SAFE_STRDUP(p, 2);	/* record-owner field */
914*0Sstevel@tonic-gate 		owner_uid = atoi(p);
915*0Sstevel@tonic-gate 		free(p);
916*0Sstevel@tonic-gate 
917*0Sstevel@tonic-gate 		if (owner_uid != euid) {
918*0Sstevel@tonic-gate 			/* re-obtain entry using owners EUID */
919*0Sstevel@tonic-gate 			free(shadow);
920*0Sstevel@tonic-gate 			nis_freeresult(res);
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate 			(void) seteuid(owner_uid);
923*0Sstevel@tonic-gate 			res = nisplus_match(name, domain, lookup,
924*0Sstevel@tonic-gate 			    sizeof (lookup));
925*0Sstevel@tonic-gate 			(void) seteuid(euid);
926*0Sstevel@tonic-gate 
927*0Sstevel@tonic-gate 			if (res == NULL)
928*0Sstevel@tonic-gate 				return (NULL);
929*0Sstevel@tonic-gate 			nret = NIS_RES_OBJECT(res);
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate 			/*CONSTANTCONDITION*/
932*0Sstevel@tonic-gate 			SAFE_STRDUP(shadow, 7);
933*0Sstevel@tonic-gate 		}
934*0Sstevel@tonic-gate 	}
935*0Sstevel@tonic-gate 
936*0Sstevel@tonic-gate 	if ((spw = calloc(1, sizeof (*spw))) == NULL) {
937*0Sstevel@tonic-gate 		nis_freeresult(res);
938*0Sstevel@tonic-gate 		return (NULL);
939*0Sstevel@tonic-gate 	}
940*0Sstevel@tonic-gate 
941*0Sstevel@tonic-gate 	SAFE_STRDUP(spw->sp_namp, 0);
942*0Sstevel@tonic-gate 	SAFE_STRDUP(spw->sp_pwdp, 1);
943*0Sstevel@tonic-gate 
944*0Sstevel@tonic-gate 	nis_freeresult(res);
945*0Sstevel@tonic-gate 
946*0Sstevel@tonic-gate 	limit = shadow + strlen(shadow) + 1;
947*0Sstevel@tonic-gate 	p = shadow;
948*0Sstevel@tonic-gate 
949*0Sstevel@tonic-gate 	spw->sp_lstchg = -1;
950*0Sstevel@tonic-gate 	spw->sp_min	= -1;
951*0Sstevel@tonic-gate 	spw->sp_max	= -1;
952*0Sstevel@tonic-gate 	spw->sp_warn	= -1;
953*0Sstevel@tonic-gate 	spw->sp_inact = -1;
954*0Sstevel@tonic-gate 	spw->sp_expire = -1;
955*0Sstevel@tonic-gate 	spw->sp_flag	= 0;
956*0Sstevel@tonic-gate 
957*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &spw->sp_lstchg))
958*0Sstevel@tonic-gate 		goto out;
959*0Sstevel@tonic-gate 
960*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &spw->sp_min))
961*0Sstevel@tonic-gate 		goto out;
962*0Sstevel@tonic-gate 
963*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &spw->sp_max))
964*0Sstevel@tonic-gate 		goto out;
965*0Sstevel@tonic-gate 
966*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &spw->sp_warn))
967*0Sstevel@tonic-gate 		goto out;
968*0Sstevel@tonic-gate 
969*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &spw->sp_inact))
970*0Sstevel@tonic-gate 		goto out;
971*0Sstevel@tonic-gate 
972*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 0, &spw->sp_expire))
973*0Sstevel@tonic-gate 		goto out;
974*0Sstevel@tonic-gate 
975*0Sstevel@tonic-gate 	if (!getfield(&p, limit, 1, &spw->sp_flag))
976*0Sstevel@tonic-gate 		goto out;
977*0Sstevel@tonic-gate 
978*0Sstevel@tonic-gate 	if (p != limit) {
979*0Sstevel@tonic-gate 		syslog(LOG_ERR, "passwdutil: garbage at end of record");
980*0Sstevel@tonic-gate 		goto error;
981*0Sstevel@tonic-gate 	}
982*0Sstevel@tonic-gate 
983*0Sstevel@tonic-gate out:
984*0Sstevel@tonic-gate 	free(shadow);
985*0Sstevel@tonic-gate 	return (spw);
986*0Sstevel@tonic-gate 
987*0Sstevel@tonic-gate error:
988*0Sstevel@tonic-gate 	if (spw->sp_namp)
989*0Sstevel@tonic-gate 		free(spw->sp_namp);
990*0Sstevel@tonic-gate 	if (spw->sp_pwdp)
991*0Sstevel@tonic-gate 		free(spw->sp_pwdp);
992*0Sstevel@tonic-gate 	free(spw);
993*0Sstevel@tonic-gate 	if (shadow)
994*0Sstevel@tonic-gate 		free(shadow);
995*0Sstevel@tonic-gate 	return (NULL);
996*0Sstevel@tonic-gate }
997