1 /* auth_unix.c -- Unix passwd file authorization
2 *
3 * Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The name "Carnegie Mellon University" must not be used to
18 * endorse or promote products derived from this software without
19 * prior written permission. For permission or any legal
20 * details, please contact
21 * Carnegie Mellon University
22 * Center for Technology Transfer and Enterprise Creation
23 * 4615 Forbes Avenue
24 * Suite 302
25 * Pittsburgh, PA 15213
26 * (412) 268-7393, fax: (412) 268-7395
27 * innovation@andrew.cmu.edu
28 *
29 * 4. Redistributions of any form whatsoever must retain the following
30 * acknowledgment:
31 * "This product includes software developed by Computing Services
32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33 *
34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41 */
42
43 #include <config.h>
44 #include <stdlib.h>
45 #include <pwd.h>
46 #include <grp.h>
47 #include <ctype.h>
48 #include <string.h>
49
50 #include "auth.h"
51 #include "libcyr_cfg.h"
52 #include "xmalloc.h"
53 #include "strarray.h"
54 #include "util.h"
55
56 struct auth_state {
57 char userid[81];
58 strarray_t groups;
59 };
60
61 static struct auth_state auth_anonymous = {
62 "anonymous", STRARRAY_INITIALIZER
63 };
64
65 /*
66 * Determine if the user is a member of 'identifier'
67 * Returns one of:
68 * 0 User does not match identifier
69 * 1 identifier matches everybody
70 * 2 User is in the group that is identifier
71 * 3 User is identifer
72 */
mymemberof(const struct auth_state * auth_state,const char * identifier)73 static int mymemberof(const struct auth_state *auth_state, const char *identifier)
74 {
75 int i;
76
77 if (!auth_state) auth_state = &auth_anonymous;
78
79 if (strcmp(identifier, "anyone") == 0) return 1;
80
81 if (strcmp(identifier, auth_state->userid) == 0) return 3;
82
83 if (strncmp(identifier, "group:", 6) != 0) return 0;
84
85 for (i=0; i<auth_state->groups.count ; i++) {
86 if (strcmp(identifier+6, auth_state->groups.data[i]) == 0) return 2;
87 }
88 return 0;
89 }
90
91 /* Map of which characters are allowed by auth_canonifyid.
92 * Key: 0 -> not allowed (special, ctrl, or would confuse Unix or imapd)
93 * 1 -> allowed, but requires an alpha somewhere else in the string
94 * 2 -> allowed, and is an alpha
95 *
96 * At least one character must be an alpha.
97 *
98 * This may not be restrictive enough.
99 * Here are the reasons for the restrictions:
100 *
101 * & forbidden because of MUTF-7. (This could be fixed.)
102 * : forbidden because it's special in /etc/passwd
103 * / forbidden because it can't be used in a mailbox name
104 * * % forbidden because they're IMAP magic in the LIST/LSUB commands
105 * ? it just scares me
106 * ctrl chars, DEL
107 * can't send them as IMAP characters in plain folder names, I think
108 * 80-FF forbidden because you can't send them in IMAP anyway
109 * (and they're forbidden as folder names). (This could be fixed.)
110 *
111 * + and - are *allowed* although '+' is probably used for userid+detail
112 * subaddressing and qmail users use '-' for subaddressing.
113 *
114 * Identifiers don't require a digit, really, so that should probably be
115 * relaxed, too.
116 */
117 static char allowedchars[256] = {
118 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00-0F */
120 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10-1F */
121 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* 20-2F */
122 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* 30-3F */
123
124 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 40-4F */
125 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, /* 50-5F */
126 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 60-6F */
127 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, /* 70-7F */
128
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
138 };
139
140 /*
141 * Convert 'identifier' into canonical form.
142 * Returns a pointer to a static buffer containing the canonical form
143 * or NULL if 'identifier' is invalid.
144 *
145 * XXX If any of the characters marked with 0 are valid and are cropping up,
146 * the right thing to do is probably to canonicalize the identifier to two
147 * representations: one for getpwent calls and one for folder names. The
148 * latter canonicalizes to a MUTF7 representation.
149 */
mycanonifyid(const char * identifier,size_t len)150 static const char *mycanonifyid(const char *identifier, size_t len)
151 {
152 static char retbuf[81];
153 struct group *grp;
154 char *p;
155 int username_tolower = 0;
156
157 if (!len) len = strlen(identifier);
158 if (len >= sizeof(retbuf)) return NULL;
159
160 memmove(retbuf, identifier, len);
161 retbuf[len] = '\0';
162
163 /* This used to be far more restrictive, but many sites seem to ignore the
164 * ye olde Unix conventions of username. Specifically, we used to
165 * - drop case on the buffer
166 * - disallow lots of non-alpha characters ('-', '_', others)
167 * Now we do neither of these, but impose a very different policy based on
168 * the character map above.
169 */
170
171 if (!strncmp(retbuf, "group:", 6)) {
172 grp = getgrnam(retbuf+6);
173 if (!grp) return NULL;
174 if (strlen(grp->gr_name) >= sizeof(retbuf)-6)
175 return NULL;
176 strcpy(retbuf+6, grp->gr_name);
177 return retbuf;
178 }
179
180 /* Copy the string and look up values in the allowedchars array above.
181 * If we see any we don't like, reject the string.
182 * Lowercase usernames if requested.
183 */
184 username_tolower = libcyrus_config_getswitch(CYRUSOPT_USERNAME_TOLOWER);
185 for (p = retbuf; *p; p++) {
186 if (username_tolower && Uisupper(*p))
187 *p = tolower((unsigned char)*p);
188
189 switch (allowedchars[*(unsigned char*) p]) {
190 case 0:
191 return NULL;
192 default:
193 break;
194 }
195 }
196
197 return retbuf;
198 }
199
200 /*
201 * Set the current user to 'identifier'. 'cacheid', if non-null,
202 * points to a 16-byte binary key to cache identifier's information
203 * with.
204 */
mynewstate(const char * identifier)205 static struct auth_state *mynewstate(const char *identifier)
206 {
207 struct auth_state *newstate;
208 struct passwd *pwd;
209 struct group *grp;
210 #if defined(HAVE_GETGROUPLIST) && defined(__GLIBC__)
211 gid_t gid, *groupids = NULL;
212 int ret, ngroups = 10, oldngroups;
213 #else
214 char **mem;
215 #endif
216
217 identifier = mycanonifyid(identifier, 0);
218 if (!identifier) return 0;
219 if (!strncmp(identifier, "group:", 6)) return 0;
220
221 newstate = (struct auth_state *)xmalloc(sizeof(struct auth_state));
222
223 strcpy(newstate->userid, identifier);
224 strarray_init(&newstate->groups);
225
226 if(!libcyrus_config_getswitch(CYRUSOPT_AUTH_UNIX_GROUP_ENABLE))
227 return newstate;
228
229 pwd = getpwnam(identifier);
230
231 #if defined(HAVE_GETGROUPLIST) && defined(__GLIBC__)
232 gid = pwd ? pwd->pw_gid : (gid_t) -1;
233
234 /* get the group ids */
235 do {
236 groupids = (gid_t *)xrealloc((gid_t *)groupids,
237 ngroups * sizeof(gid_t));
238
239 oldngroups = ngroups; /* copy of ngroups for comparison */
240 ret = getgrouplist(identifier, gid, groupids, &ngroups);
241 /*
242 * This is tricky. We do this as long as getgrouplist tells us to
243 * realloc _and_ the number of groups changes. It tells us to realloc
244 * also in the case of failure...
245 */
246 } while (ret == -1 && ngroups != oldngroups);
247
248 if (ret == -1)
249 goto err;
250
251 while (ngroups--) {
252 if (pwd || groupids[ngroups] != gid) {
253 if ((grp = getgrgid(groupids[ngroups])))
254 strarray_append(&newstate->groups, grp->gr_name);
255 }
256 }
257
258 err:
259 if (groupids) free(groupids);
260
261 #else /* !HAVE_GETGROUPLIST */
262 setgrent();
263 while ((grp = getgrent())) {
264 for (mem = grp->gr_mem; *mem; mem++) {
265 if (!strcmp(*mem, identifier)) break;
266 }
267
268 if (*mem || (pwd && pwd->pw_gid == grp->gr_gid))
269 strarray_append(&newstate->groups, grp->gr_name);
270 }
271 endgrent();
272 #endif /* HAVE_GETGROUPLIST */
273
274 return newstate;
275 }
276
myfreestate(struct auth_state * auth_state)277 static void myfreestate(struct auth_state *auth_state)
278 {
279 strarray_fini(&auth_state->groups);
280 free(auth_state);
281 }
282
mygroups(const struct auth_state * auth_state)283 static strarray_t *mygroups(const struct auth_state *auth_state)
284 {
285 return strarray_dup(&auth_state->groups);
286 }
287
288 HIDDEN struct auth_mech auth_unix =
289 {
290 "unix", /* name */
291
292 &mycanonifyid,
293 &mymemberof,
294 &mynewstate,
295 &myfreestate,
296 &mygroups,
297 };
298