Mercurial > dovecot > core-2.2
annotate src/auth/userinfo-passwd-file.c @ 184:4223b9ed0c80 HEAD
move size_t fixes
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 08 Sep 2002 17:39:05 +0300 |
parents | a3d77e73f99b |
children | c2269ed9ffcb |
rev | line source |
---|---|
0 | 1 /* Copyright (C) 2002 Timo Sirainen */ |
2 | |
3 #define _XOPEN_SOURCE 4 | |
4 #define _XOPEN_SOURCE_EXTENDED | |
5 #define _XPG4_2 | |
6 | |
7 #include "common.h" | |
8 | |
9 #ifdef USERINFO_PASSWD_FILE | |
10 | |
11 #include "iobuffer.h" | |
12 #include "hash.h" | |
13 #include "hex-binary.h" | |
14 #include "md5.h" | |
15 #include "userinfo.h" | |
16 #include "userinfo-passwd.h" | |
17 | |
18 #include <stdlib.h> | |
19 #include <unistd.h> | |
20 #include <fcntl.h> | |
21 #include <sys/stat.h> | |
22 | |
23 typedef struct { | |
24 Pool pool; | |
25 | |
26 char *path; | |
27 time_t stamp; | |
28 int fd; | |
29 | |
30 HashTable *users; | |
31 } PasswdFile; | |
32 | |
33 typedef enum { | |
34 PASSWORD_DES, | |
35 PASSWORD_MD5, | |
36 PASSWORD_DIGEST_MD5 | |
37 } PasswordType; | |
38 | |
39 typedef struct { | |
40 char *user_realm; /* user:realm */ | |
41 const char *realm; /* NULL or points to user_realm */ | |
42 char *password; | |
43 char *home; | |
44 char *mail; | |
45 | |
46 uid_t uid; | |
47 gid_t gid; | |
48 | |
49 PasswordType password_type; | |
50 unsigned int chroot:1; | |
51 } PasswdUser; | |
52 | |
53 static PasswdFile *passwd_file; | |
54 | |
55 static int get_reply_data(PasswdUser *pu, AuthCookieReplyData *reply) | |
56 { | |
57 const char *user; | |
58 struct passwd *pw; | |
59 | |
60 if (pu->uid == 0 || pu->gid == 0 || | |
61 (pu->home == NULL && pu->mail == NULL)) { | |
62 /* all required information was not set in passwd file, | |
63 check from system's passwd */ | |
64 user = pu->realm == NULL ? pu->user_realm : | |
65 t_strndup(pu->user_realm, strlen(pu->user_realm) - | |
66 strlen(pu->realm) - 1); | |
67 | |
68 pw = getpwnam(user); | |
69 if (pw == NULL) | |
70 return FALSE; | |
71 | |
72 passwd_fill_cookie_reply(pw, reply); | |
73 } | |
74 | |
75 if (pu->uid != 0) | |
76 reply->uid = pu->uid; | |
77 if (pu->gid != 0) | |
78 reply->gid = pu->gid; | |
79 | |
80 if (pu->home != NULL) { | |
81 i_assert(sizeof(reply->home) > strlen(pu->home)); | |
82 strcpy(reply->home, pu->home); | |
83 } | |
84 | |
85 if (pu->mail != NULL) { | |
86 i_assert(sizeof(reply->mail) > strlen(pu->mail)); | |
87 strcpy(reply->mail, pu->mail); | |
88 } | |
89 | |
90 reply->chroot = pu->chroot; | |
91 return TRUE; | |
92 } | |
93 | |
94 static int passwd_file_verify_plain(const char *user, const char *password, | |
95 AuthCookieReplyData *reply) | |
96 { | |
97 PasswdUser *pu; | |
98 char *const *tmp; | |
99 unsigned char digest[16]; | |
100 const char *str; | |
101 | |
102 /* find it from all realms */ | |
103 pu = hash_lookup(passwd_file->users, user); | |
104 if (pu == NULL) { | |
105 t_push(); | |
106 for (tmp = auth_realms; *tmp != NULL; tmp++) { | |
107 str = t_strconcat(user, ":", *tmp, NULL); | |
108 pu = hash_lookup(passwd_file->users, str); | |
109 } | |
110 t_pop(); | |
111 } | |
112 | |
113 if (pu == NULL) | |
114 return FALSE; | |
115 | |
116 /* verify that password matches */ | |
117 switch (pu->password_type) { | |
118 case PASSWORD_DES: | |
119 if (strcmp(crypt(password, pu->password), pu->password) != 0) | |
120 return FALSE; | |
121 break; | |
122 case PASSWORD_MD5: | |
123 md5_get_digest(password, strlen(password), digest); | |
124 str = binary_to_hex(digest, sizeof(digest)); | |
125 | |
126 if (strcmp(str, pu->password) != 0) | |
127 return FALSE; | |
128 break; | |
129 case PASSWORD_DIGEST_MD5: | |
130 /* user:realm:passwd */ | |
131 str = t_strconcat(pu->user_realm, | |
132 pu->realm == NULL ? ":" : "", ":", | |
133 password, NULL); | |
134 | |
135 md5_get_digest(str, strlen(str), digest); | |
136 str = binary_to_hex(digest, sizeof(digest)); | |
137 | |
138 if (strcmp(str, pu->password) != 0) | |
139 return FALSE; | |
140 break; | |
141 default: | |
142 i_assert(0); | |
143 } | |
144 | |
145 /* found */ | |
146 return get_reply_data(pu, reply); | |
147 } | |
148 | |
149 static int passwd_file_lookup_digest_md5(const char *user, const char *realm, | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
150 unsigned char digest[16], |
0 | 151 AuthCookieReplyData *reply) |
152 { | |
153 const char *id; | |
154 PasswdUser *pu; | |
155 | |
156 /* FIXME: we simply ignore UTF8 setting.. */ | |
157 | |
158 id = realm == NULL || *realm == '\0' ? user : | |
159 t_strconcat(user, ":", realm, NULL); | |
160 | |
161 pu = hash_lookup(passwd_file->users, id); | |
162 if (pu == NULL) | |
163 return FALSE; | |
164 | |
165 /* found */ | |
166 i_assert(strlen(pu->password) == 32); | |
167 if (!hex_to_binary(pu->password, digest)) | |
168 return FALSE; | |
169 | |
170 return get_reply_data(pu, reply); | |
171 } | |
172 | |
173 static void passwd_file_add(PasswdFile *pw, const char *username, | |
174 const char *pass, char *const *args) | |
175 { | |
176 /* args = uid, gid, user info, home dir, shell, realm, mail, chroot */ | |
177 PasswdUser *pu; | |
178 const char *p; | |
179 | |
180 if (strlen(username) >= AUTH_MAX_USER_LEN) { | |
181 i_error("Username %s is too long (max. %d chars) in password " | |
182 "file %s", username, AUTH_MAX_USER_LEN, pw->path); | |
183 return; | |
184 } | |
185 | |
186 pu = p_new(pw->pool, PasswdUser, 1); | |
187 | |
188 p = strchr(pass, '['); | |
189 if (p == NULL) { | |
190 pu->password = p_strdup(pw->pool, pass); | |
191 pu->password_type = PASSWORD_DES; | |
192 } else { | |
193 /* password[type] - we're being libpam-pwdfile compatible | |
194 here. it uses 13 = DES and 34 = MD5. We add | |
195 56 = Digest-MD5. */ | |
184 | 196 pu->password = p_strndup(pw->pool, pass, (size_t) (p-pass)); |
0 | 197 if (p[1] == '3' && p[2] == '4') { |
198 pu->password_type = PASSWORD_MD5; | |
199 str_lcase(pu->password); | |
200 } else if (p[1] == '5' && p[2] == '6') { | |
201 pu->password_type = PASSWORD_DIGEST_MD5; | |
202 if (strlen(pu->password) != 32) { | |
203 i_error("User %s has invalid password in " | |
204 "file %s", username, pw->path); | |
205 return; | |
206 } | |
207 str_lcase(pu->password); | |
208 } else { | |
209 pu->password_type = PASSWORD_DES; | |
210 } | |
211 } | |
212 | |
213 if (args[0] != NULL) { | |
214 pu->uid = atoi(args[0]); | |
215 if (pu->uid == 0) { | |
216 i_error("User %s has UID 0 in password file %s", | |
217 username, pw->path); | |
218 return; | |
219 } | |
220 args++; | |
221 } | |
222 | |
223 if (args[0] != NULL) { | |
224 pu->gid = atoi(args[0]); | |
225 if (pu->gid == 0) { | |
226 i_error("User %s has GID 0 in password file %s", | |
227 username, pw->path); | |
228 return; | |
229 } | |
230 args++; | |
231 } | |
232 | |
233 /* user info */ | |
234 if (args[0] != NULL) | |
235 args++; | |
236 | |
237 /* home */ | |
238 if (args[0] != NULL) { | |
239 if (strlen(args[0]) >= AUTH_MAX_HOME_LEN) { | |
240 i_error("User %s has too long home directory in " | |
241 "password file %s", username, pw->path); | |
242 return; | |
243 } | |
244 | |
245 pu->home = p_strdup(pw->pool, args[0]); | |
246 args++; | |
247 } | |
248 | |
249 /* shell */ | |
250 if (args[0] != NULL) | |
251 args++; | |
252 | |
253 /* realm */ | |
254 if (args[0] == NULL || *args[0] == '\0') { | |
255 pu->user_realm = p_strdup(pw->pool, username); | |
256 if (hash_lookup(pw->users, username) != NULL) { | |
257 i_error("User %s already exists in password file %s", | |
258 username, pw->path); | |
259 return; | |
260 } | |
261 } else { | |
262 pu->user_realm = p_strconcat(pw->pool, username, ":", | |
263 args[0], NULL); | |
264 pu->realm = pu->user_realm + strlen(username)+1; | |
265 | |
266 if (hash_lookup(pw->users, pu->user_realm) != NULL) { | |
267 i_error("User %s already exists in realm %s in " | |
268 "password file %s", username, args[0], | |
269 pw->path); | |
270 return; | |
271 } | |
272 } | |
273 | |
274 /* mail storage */ | |
275 if (args[0] != NULL) { | |
276 if (strlen(args[0]) >= AUTH_MAX_MAIL_LEN) { | |
277 i_error("User %s has too long mail storage in " | |
278 "password file %s", username, pw->path); | |
279 return; | |
280 } | |
281 | |
282 pu->mail = p_strdup(pw->pool, args[0]); | |
283 args++; | |
284 } | |
285 | |
286 /* chroot */ | |
287 if (args[0] != NULL && strstr(args[0], "chroot") != NULL) | |
288 pu->chroot = TRUE; | |
289 | |
290 hash_insert(pw->users, pu->user_realm, pu); | |
291 } | |
292 | |
293 static void passwd_file_parse_file(PasswdFile *pw) | |
294 { | |
295 IOBuffer *inbuf; | |
296 char *const *args; | |
297 char *line; | |
298 | |
299 inbuf = io_buffer_create_file(pw->fd, default_pool, 2048); | |
300 for (;;) { | |
301 line = io_buffer_next_line(inbuf); | |
302 if (line == NULL) { | |
303 if (io_buffer_read(inbuf) <= 0) | |
304 break; | |
305 continue; | |
306 } | |
307 | |
308 if (*line == '\0' || *line == ':') | |
309 continue; /* no username */ | |
310 | |
311 t_push(); | |
312 args = t_strsplit(line, ":"); | |
313 if (args[1] != NULL && IS_VALID_PASSWD(args[1])) { | |
314 /* valid user/pass */ | |
315 passwd_file_add(pw, args[0], args[1], args+2); | |
316 } | |
317 t_pop(); | |
318 } | |
319 io_buffer_destroy(inbuf); | |
320 } | |
321 | |
322 static PasswdFile *passwd_file_parse(const char *path) | |
323 { | |
324 PasswdFile *pw; | |
325 Pool pool; | |
326 struct stat st; | |
327 int fd; | |
328 | |
329 fd = open(path, O_RDONLY); | |
330 if (fd == -1) { | |
331 i_fatal("Can't open passwd-file %s: %m", path); | |
332 } | |
333 | |
334 if (fstat(fd, &st) != 0) | |
335 i_fatal("fstat() failed for passwd-file %s: %m", path); | |
336 | |
337 pool = pool_create("PasswdFile", 10240, FALSE); | |
338 pw = p_new(pool, PasswdFile, 1); | |
339 pw->pool = pool; | |
340 pw->path = p_strdup(pool, path); | |
341 pw->stamp = st.st_mtime; | |
342 pw->fd = fd; | |
343 pw->users = hash_create(pool, 100, str_hash, (HashCompareFunc) strcmp); | |
344 | |
345 passwd_file_parse_file(pw); | |
346 return pw; | |
347 } | |
348 | |
349 static void passwd_file_free(PasswdFile *pw) | |
350 { | |
351 pool_unref(pw->pool); | |
352 } | |
353 | |
354 static void passwd_file_init(const char *args) | |
355 { | |
356 passwd_file = passwd_file_parse(args); | |
357 } | |
358 | |
359 static void passwd_file_deinit(void) | |
360 { | |
361 passwd_file_free(passwd_file); | |
362 } | |
363 | |
364 UserInfoModule userinfo_passwd_file = { | |
365 passwd_file_init, | |
366 passwd_file_deinit, | |
367 | |
368 passwd_file_verify_plain, | |
369 passwd_file_lookup_digest_md5 | |
370 }; | |
371 | |
372 #endif |