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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * files/getpwnam.c -- "files" backend for nsswitch "passwd" database
26  */
27 
28 #include <pwd.h>
29 #include <shadow.h>
30 #include <unistd.h>		/* for PF_PATH */
31 #include "files_common.h"
32 #include <strings.h>
33 #include <stdlib.h>
34 
35 static uint_t
hash_pwname(nss_XbyY_args_t * argp,int keyhash,const char * line,int linelen)36 hash_pwname(nss_XbyY_args_t *argp, int keyhash, const char *line,
37 	int linelen)
38 {
39 	const char	*name;
40 	int		namelen, i;
41 	uint_t 		hash = 0;
42 
43 	if (keyhash) {
44 		name = argp->key.name;
45 		namelen = strlen(name);
46 	} else {
47 		name = line;
48 		namelen = 0;
49 		while (linelen-- && *line++ != ':')
50 			namelen++;
51 	}
52 
53 	for (i = 0; i < namelen; i++)
54 		hash = hash * 15 + name[i];
55 	return (hash);
56 }
57 
58 static uint_t
hash_pwuid(nss_XbyY_args_t * argp,int keyhash,const char * line,int linelen)59 hash_pwuid(nss_XbyY_args_t *argp, int keyhash, const char *line,
60 	int linelen)
61 {
62 	uint_t		id;
63 	const char	*linep, *limit, *end;
64 
65 	linep = line;
66 	limit = line + linelen;
67 
68 	if (keyhash)
69 		return ((uint_t)argp->key.uid);
70 
71 	while (linep < limit && *linep++ != ':') /* skip username */
72 		continue;
73 	while (linep < limit && *linep++ != ':') /* skip password */
74 		continue;
75 	if (linep == limit)
76 		return (UID_NOBODY);
77 
78 	/* uid */
79 	end = linep;
80 	id = (uint_t)strtoul(linep, (char **)&end, 10);
81 
82 	/* empty uid */
83 	if (linep == end)
84 		return (UID_NOBODY);
85 
86 	return (id);
87 }
88 
89 static files_hash_func hash_pw[2] = { hash_pwname, hash_pwuid };
90 
91 static files_hash_t hashinfo = {
92 	DEFAULTMUTEX,
93 	sizeof (struct passwd),
94 	NSS_BUFLEN_PASSWD,
95 	2,
96 	hash_pw
97 };
98 
99 static int
check_pwname(nss_XbyY_args_t * argp,const char * line,int linelen)100 check_pwname(nss_XbyY_args_t *argp, const char *line, int linelen)
101 {
102 	const char	*linep, *limit;
103 	const char *keyp = argp->key.name;
104 
105 	linep = line;
106 	limit = line + linelen;
107 
108 	/* +/- entries valid for compat source only */
109 	if (linelen == 0 || *line == '+' || *line == '-')
110 		return (0);
111 	while (*keyp && linep < limit && *keyp == *linep) {
112 		keyp++;
113 		linep++;
114 	}
115 	return (linep < limit && *keyp == '\0' && *linep == ':');
116 }
117 
118 static nss_status_t
getbyname(be,a)119 getbyname(be, a)
120 	files_backend_ptr_t	be;
121 	void			*a;
122 {
123 	return (_nss_files_XY_hash(be, a, 0, &hashinfo, 0, check_pwname));
124 }
125 
126 static int
check_pwuid(nss_XbyY_args_t * argp,const char * line,int linelen)127 check_pwuid(nss_XbyY_args_t *argp, const char *line, int linelen)
128 {
129 	const char	*linep, *limit, *end;
130 	ulong_t		pw_uid;
131 
132 	linep = line;
133 	limit = line + linelen;
134 
135 	/* +/- entries valid for compat source only */
136 	if (linelen == 0 || *line == '+' || *line == '-')
137 		return (0);
138 
139 	while (linep < limit && *linep++ != ':') /* skip username */
140 		continue;
141 	while (linep < limit && *linep++ != ':') /* skip password */
142 		continue;
143 	if (linep == limit)
144 		return (0);
145 
146 	/* uid */
147 	end = linep;
148 	pw_uid = strtoul(linep, (char **)&end, 10);
149 
150 	/* check if the uid is empty or overflows */
151 	if (linep == end || pw_uid > UINT32_MAX)
152 		return (0);
153 
154 	return ((uid_t)pw_uid == argp->key.uid);
155 }
156 
157 static nss_status_t
getbyuid(be,a)158 getbyuid(be, a)
159 	files_backend_ptr_t	be;
160 	void			*a;
161 {
162 	nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
163 
164 	if (argp->key.uid > MAXUID)
165 		return (NSS_NOTFOUND);
166 	return (_nss_files_XY_hash(be, argp, 0, &hashinfo, 1, check_pwuid));
167 }
168 
169 /*
170  * Validates passwd entry replacing uid/gid > MAXUID by ID_NOBODY.
171  */
172 int
validate_passwd_ids(char * line,int * linelenp,int buflen,int extra_chars)173 validate_passwd_ids(char *line, int *linelenp, int buflen, int extra_chars)
174 {
175 	char	*linep, *limit, *uidp, *gidp;
176 	uid_t	uid;
177 	gid_t	gid;
178 	ulong_t	uidl, gidl;
179 	int	olduidlen, oldgidlen, idlen;
180 	int	linelen = *linelenp, newlinelen;
181 
182 	/*
183 	 * +name entries in passwd(4) do not override uid and gid
184 	 * values. Therefore no need to validate the ids in these
185 	 * entries.
186 	 */
187 	if (linelen == 0 || *line == '+' || *line == '-')
188 		return (NSS_STR_PARSE_SUCCESS);
189 
190 	linep = line;
191 	limit = line + linelen;
192 
193 	while (linep < limit && *linep++ != ':') /* skip username */
194 		continue;
195 	while (linep < limit && *linep++ != ':') /* skip password */
196 		continue;
197 	if (linep == limit)
198 		return (NSS_STR_PARSE_PARSE);
199 
200 	uidp = linep;
201 	uidl = strtoul(uidp, (char **)&linep, 10); /* grab uid */
202 	olduidlen = linep - uidp;
203 	if (++linep >= limit || olduidlen == 0)
204 		return (NSS_STR_PARSE_PARSE);
205 
206 	gidp = linep;
207 	gidl = strtoul(gidp, (char **)&linep, 10); /* grab gid */
208 	oldgidlen = linep - gidp;
209 	if (linep >= limit || oldgidlen == 0)
210 		return (NSS_STR_PARSE_PARSE);
211 
212 	if (uidl <= MAXUID && gidl <= MAXUID)
213 		return (NSS_STR_PARSE_SUCCESS);
214 	uid = (uidl > MAXUID) ? UID_NOBODY : (uid_t)uidl;
215 	gid = (gidl > MAXUID) ? GID_NOBODY : (gid_t)gidl;
216 
217 	/* Check if we have enough space in the buffer */
218 	idlen = snprintf(NULL, 0, "%u:%u", uid, gid);
219 	newlinelen = linelen + idlen - olduidlen - oldgidlen - 1;
220 	if (newlinelen + extra_chars > buflen)
221 		return (NSS_STR_PARSE_ERANGE);
222 
223 	/* Replace ephemeral ids by ID_NOBODY */
224 	(void) bcopy(linep, uidp + idlen, limit - linep + extra_chars);
225 	(void) snprintf(uidp, idlen + 1, "%u:%u", uid, gid);
226 	*(uidp + idlen) = ':'; /* restore : that was overwritten by snprintf */
227 	*linelenp = newlinelen;
228 	return (NSS_STR_PARSE_SUCCESS);
229 }
230 
231 static files_backend_op_t passwd_ops[] = {
232 	_nss_files_destr,
233 	_nss_files_endent,
234 	_nss_files_setent,
235 	_nss_files_getent_rigid,
236 	getbyname,
237 	getbyuid
238 };
239 
240 /*ARGSUSED*/
241 nss_backend_t *
_nss_files_passwd_constr(dummy1,dummy2,dummy3)242 _nss_files_passwd_constr(dummy1, dummy2, dummy3)
243 	const char	*dummy1, *dummy2, *dummy3;
244 {
245 	return (_nss_files_constr(passwd_ops,
246 				sizeof (passwd_ops) / sizeof (passwd_ops[0]),
247 				PF_PATH,
248 				NSS_LINELEN_PASSWD,
249 				&hashinfo));
250 }
251