1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  *	getpwent.c
26  *
27  * lib/nsswitch/compat/getpwent.c -- name-service-switch backend for getpwnam()
28  *   et al that does 4.x compatibility.  It looks in /etc/passwd; if it finds
29  *   passwd entries there that begin with "+" or "-", it consults other
30  *   services.  By default it uses NIS (YP), but the user can override this
31  *   with a "passwd_compat" entry in /etc/nsswitch.conf, e.g.
32  *			passwd_compat: nisplus
33  *
34  * This code tries to produce the same results as the 4.x code, even when
35  *   the latter seems ill thought-out (mostly in the handling of netgroups,
36  *   "-", and the combination thereof).  Bug-compatible, in other words.
37  *   Though we do try to be more reasonable about the format of "+" and "-"
38  *   entries here, i.e. you don't have to pad them with spurious colons and
39  *   bogus uid/gid values.
40  *
41  * Caveats:
42  *    -	More than one source may be specified, with the usual switch semantics,
43  *	but having multiple sources here is definitely odd.
44  *    -	People who recursively specify "compat" deserve what they get.
45  *    -	Entries that begin with "+@" or "-@" are interpreted using
46  *	getnetgrent() and innetgr(), which use the "netgroup" entry in
47  *	/etc/nsswitch.conf.  If the sources for "passwd_compat" and "netgroup"
48  *	differ, everything should work fine, but the semantics will be pretty
49  *	confusing.
50  */
51 
52 #pragma ident	"%Z%%M%	%I%	%E% SMI"
53 
54 #include <pwd.h>
55 #include <shadow.h>		/* For PASSWD (pathname to passwd file) */
56 #include <stdlib.h>
57 #include <strings.h>
58 #include "compat_common.h"
59 
60 static DEFINE_NSS_DB_ROOT(db_root);
61 
62 static void
63 _nss_initf_passwd_compat(p)
64 	nss_db_params_t	*p;
65 {
66 	p->name		  = NSS_DBNAM_PASSWD;
67 	p->config_name	  = NSS_DBNAM_PASSWD_COMPAT;
68 	p->default_config = NSS_DEFCONF_PASSWD_COMPAT;
69 }
70 
71 static const char *
72 get_pwname(argp)
73 	nss_XbyY_args_t		*argp;
74 {
75 	struct passwd		*p = (struct passwd *)argp->returnval;
76 
77 	return (p->pw_name);
78 }
79 
80 static int
81 check_pwname(argp)
82 	nss_XbyY_args_t		*argp;
83 {
84 	struct passwd		*p = (struct passwd *)argp->returnval;
85 
86 	return (strcmp(p->pw_name, argp->key.name) == 0);
87 }
88 
89 static nss_status_t
90 getbyname(be, a)
91 	compat_backend_ptr_t	be;
92 	void			*a;
93 {
94 	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
95 
96 	return (_nss_compat_XY_all(be, argp,
97 				check_pwname, NSS_DBOP_PASSWD_BYNAME));
98 }
99 
100 static int
101 check_pwuid(argp)
102 	nss_XbyY_args_t		*argp;
103 {
104 	struct passwd		*p = (struct passwd *)argp->returnval;
105 
106 	return (p->pw_uid == argp->key.uid);
107 }
108 
109 static nss_status_t
110 getbyuid(be, a)
111 	compat_backend_ptr_t	be;
112 	void			*a;
113 {
114 	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
115 
116 	return (_nss_compat_XY_all(be, argp,
117 				check_pwuid, NSS_DBOP_PASSWD_BYUID));
118 }
119 
120 /*ARGSUSED*/
121 static int
122 merge_pwents(be, argp, fields)
123 	compat_backend_ptr_t	be;
124 	nss_XbyY_args_t		*argp;
125 	const char		**fields;
126 {
127 	struct passwd		*pw	= (struct passwd *)argp->buf.result;
128 	char			*buf	= malloc(NSS_LINELEN_PASSWD);
129 	char			*s;
130 	int			parsestat;
131 	int			len;
132 	int			buflen;
133 
134 	if (buf == 0) {
135 		return (NSS_STR_PARSE_PARSE);
136 		/* Really "out of memory", but PARSE_PARSE will have to do */
137 	}
138 	/*
139 	 * Don't allow overriding of
140 	 *	- username
141 	 *	- uid
142 	 *	- gid
143 	 * That's what the SunOS 4.x code did;  who are we to question it...
144 	 */
145 	s = buf;
146 	buflen = argp->buf.buflen;
147 
148 	if (fields[1] != 0)
149 		len = snprintf(s, buflen, "%s:%s",
150 				pw->pw_name, fields[1]);
151 	else {
152 /* ====> Does this do the right thing? */
153 		if (pw->pw_age != 0 && *pw->pw_age != '\0')
154 			len = snprintf(s, buflen, "%s:%s,%s",
155 				pw->pw_name, pw->pw_passwd, pw->pw_age);
156 		else
157 			len = snprintf(s, buflen, "%s:%s",
158 				pw->pw_name, pw->pw_passwd);
159 	}
160 
161 	if (len > buflen)
162 		return (NSS_STR_PARSE_ERANGE);
163 
164 	s += len;
165 	buflen -= len;
166 	len = snprintf(s, buflen, ":%ld:%ld:%s:%s:%s",
167 		pw->pw_uid,
168 		pw->pw_gid,
169 		fields[4] != 0 ? fields[4] : pw->pw_gecos,
170 		fields[5] != 0 ? fields[5] : pw->pw_dir,
171 		fields[6] != 0 ? fields[6] : pw->pw_shell);
172 
173 	if (len > buflen)
174 		return (NSS_STR_PARSE_ERANGE);
175 
176 	s += len;
177 	len = s - buf;
178 
179 	/*
180 	 * if asked, return the data in /etc file format
181 	 */
182 	if (be->return_string_data == 1) {
183 		/* reset the result ptr to the original value */
184 		argp->buf.result = NULL;
185 
186 		if (len > argp->buf.buflen) {
187 			parsestat = NSS_STR_PARSE_ERANGE;
188 		} else {
189 			(void) strncpy(argp->buf.buffer, buf, len);
190 			argp->returnval = argp->buf.buffer;
191 			argp->returnlen = len;
192 			parsestat = NSS_SUCCESS;
193 		}
194 	} else {
195 		parsestat = (*argp->str2ent)(buf, len,
196 				    argp->buf.result,
197 				    argp->buf.buffer,
198 				    argp->buf.buflen);
199 	}
200 	free(buf);
201 	return (parsestat);
202 }
203 
204 static compat_backend_op_t passwd_ops[] = {
205 	_nss_compat_destr,
206 	_nss_compat_endent,
207 	_nss_compat_setent,
208 	_nss_compat_getent,
209 	getbyname,
210 	getbyuid
211 };
212 
213 /*ARGSUSED*/
214 nss_backend_t *
215 _nss_compat_passwd_constr(dummy1, dummy2, dummy3)
216 	const char	*dummy1, *dummy2, *dummy3;
217 {
218 	return (_nss_compat_constr(passwd_ops,
219 				sizeof (passwd_ops) / sizeof (passwd_ops[0]),
220 				PASSWD,
221 				NSS_LINELEN_PASSWD,
222 				&db_root,
223 				_nss_initf_passwd_compat,
224 				1,
225 				get_pwname,
226 				merge_pwents));
227 }
228