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