3 * Copyright (c) 1990,1993 Regents of The University of Michigan.
4 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
20 #include <sys/param.h>
21 #include <arpa/inet.h>
23 #include <atalk/logger.h>
24 #include <atalk/afp.h>
25 #include <atalk/uam.h>
32 #endif /* USE_CRACKLIB */
36 static C_Block seskey;
37 static Key_schedule seskeysched;
38 static struct passwd *randpwd;
39 static uint8_t randbuf[8];
41 /* hash to a 16-bit number. this will generate completely harmless
42 * warnings on 64-bit machines. */
43 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
44 ((unsigned long)a)) & 0xffff)
47 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
48 static int home_passwd(const struct passwd *pwd,
49 const char *path, const int pathlen _U_,
50 unsigned char *passwd, const int len,
56 if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
57 LOG(log_error, logtype_uams, "Failed to open %s", path);
61 if ( fstat( fd, &st ) < 0 )
62 goto home_passwd_fail;
64 /* If any of these are true, disallow login:
65 * - not a regular file
66 * - gid or uid don't match user
67 * - anyone else has permissions of any sort
69 if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
70 (pwd->pw_gid != st.st_gid) ||
71 (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
72 LOG(log_info, logtype_uams, "Insecure permissions found for %s.", path);
73 goto home_passwd_fail;
76 /* get the password */
78 if (write(fd, passwd, len) < 0) {
79 LOG(log_error, logtype_uams, "Failed to write to %s", path );
80 goto home_passwd_fail;
83 if (read(fd, passwd, len) < 0) {
84 LOG(log_error, logtype_uams, "Failed to read from %s", path );
85 goto home_passwd_fail;
88 /* get rid of pesky characters */
89 for (i = 0; i < len; i++)
90 if ((passwd[i] != ' ') && isspace(passwd[i]))
105 * handle /path/afppasswd with an optional key file. we're a lot more
106 * trusting of this file. NOTE: we use our own password entry writing
107 * bits as we want to avoid tromping over global variables. in addition,
108 * we look for a key file and use that if it's there. here are the
111 * username:password:last login date:failedcount
113 * password is just the hex equivalent of either the ASCII password
114 * (if the key file doesn't exist) or the des encrypted password.
118 #define PASSWD_ILLEGAL '*'
119 #define unhex(x) (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
120 static int afppasswd(const struct passwd *pwd,
121 const char *path, const int pathlen,
122 unsigned char *passwd, int len,
125 uint8_t key[DES_KEY_SZ*2];
126 char buf[MAXPATHLEN + 1], *p;
127 Key_schedule schedule;
130 int keyfd = -1, err = 0;
133 if ((fp = fopen(path, (set) ? "r+" : "r")) == NULL) {
134 LOG(log_error, logtype_uams, "Failed to open %s", path);
135 return AFPERR_ACCESS;
138 /* open the key file if it exists */
140 if (pathlen < (int) sizeof(buf) - 5) {
142 keyfd = open(buf, O_RDONLY);
146 memset(buf, 0, sizeof(buf));
147 while (fgets(buf, sizeof(buf), fp)) {
148 if ((p = strchr(buf, ':'))) {
149 if ( strlen(pwd->pw_name) == (p - buf) &&
150 strncmp(buf, pwd->pw_name, p - buf) == 0) {
152 if (*p == PASSWD_ILLEGAL) {
153 LOG(log_info, logtype_uams, "invalid password entry for %s", pwd->pw_name);
157 goto afppasswd_found;
161 memset(buf, 0, sizeof(buf));
168 /* convert to binary. */
169 for (i = j = 0; i < sizeof(key); i += 2, j++)
170 p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
172 memset(p + j, 0, sizeof(key) - j);
176 /* read in the hex representation of an 8-byte key */
177 read(keyfd, key, sizeof(key));
179 /* convert to binary key */
180 for (i = j = 0; i < strlen((char *) key); i += 2, j++)
181 key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
183 memset(key + j, 0, sizeof(key) - j);
184 key_sched((C_Block *) key, schedule);
185 memset(key, 0, sizeof(key));
188 /* NOTE: this takes advantage of the fact that passwd doesn't
189 * get used after this call if it's being set. */
190 ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
193 /* decrypt the password */
194 ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
196 memset(&schedule, 0, sizeof(schedule));
200 const unsigned char hextable[] = "0123456789ABCDEF";
204 /* convert to hex password */
205 for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
206 key[j] = hextable[(passwd[i] & 0xF0) >> 4];
207 key[j + 1] = hextable[passwd[i] & 0x0F];
209 memcpy(p, key, sizeof(key));
211 /* get exclusive access to the user's password entry. we don't
212 * worry so much on reads. in the worse possible case there, the
213 * user will just need to re-enter their password. */
214 lock.l_type = F_WRLCK;
217 lock.l_whence = SEEK_SET;
219 fseek(fp, pos, SEEK_SET);
220 fcntl(fd, F_SETLKW, &lock);
221 fwrite(buf, p - buf + sizeof(key), 1, fp);
222 lock.l_type = F_UNLCK;
223 fcntl(fd, F_SETLK, &lock);
225 memcpy(passwd, p, len);
227 memset(buf, 0, sizeof(buf));
237 /* this sets the uid. it needs to do slightly different things
238 * depending upon whether or not the password is in ~/.passwd
239 * or in a global location */
240 static int randpass(const struct passwd *pwd, const char *file,
241 unsigned char *passwd, const int len, const int set)
244 uid_t uid = geteuid();
246 /* Build pathname to user's '.passwd' file */
249 char path[MAXPATHLEN + 1];
251 if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
254 strcpy(path, pwd->pw_dir );
256 strcat(path, file + 2);
258 seteuid(pwd->pw_uid); /* change ourselves to the user */
259 i = home_passwd(pwd, path, i, passwd, len, set);
261 seteuid(0); /* change ourselves back to root */
268 /* handle afppasswd file. we need to make sure that we're root
269 * when we do this. */
272 i = afppasswd(pwd, file, i, passwd, len, set);
278 /* randnum sends an 8-byte number and uses the user's password to
279 * check against the encrypted reply. */
280 static int rand_login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
281 char *ibuf _U_, size_t ibuflen _U_,
282 char *rbuf, size_t *rbuflen)
290 if (( randpwd = uam_getname(obj, username, ulen)) == NULL )
291 return AFPERR_NOTAUTH; /* unknown user */
293 LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
294 if (uam_checkuser(randpwd) < 0)
295 return AFPERR_NOTAUTH;
297 len = UAM_PASSWD_FILENAME;
298 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
299 (void *) &passwdfile, &len) < 0)
302 if ((err = randpass(randpwd, passwdfile, seskey,
303 sizeof(seskey), 0)) != AFP_OK)
306 /* get a random number */
307 len = sizeof(randbuf);
308 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
309 (void *) randbuf, &len) < 0)
312 /* session id is a hashed version of the obj pointer */
313 sessid = randhash(obj);
314 memcpy(rbuf, &sessid, sizeof(sessid));
315 rbuf += sizeof(sessid);
316 *rbuflen = sizeof(sessid);
318 /* send the random number off */
319 memcpy(rbuf, randbuf, sizeof(randbuf));
320 *rbuflen += sizeof(randbuf);
321 return AFPERR_AUTHCONT;
325 /* check encrypted reply. we actually setup the encryption stuff
326 * here as the first part of randnum and rand2num are identical. */
327 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
328 char *ibuf, size_t ibuflen _U_,
329 char *rbuf _U_, size_t *rbuflen)
335 memcpy(&sessid, ibuf, sizeof(sessid));
336 if (sessid != randhash(obj))
339 ibuf += sizeof(sessid);
341 /* encrypt. this saves a little space by using the fact that
342 * des can encrypt in-place without side-effects. */
343 key_sched((C_Block *) seskey, seskeysched);
344 memset(seskey, 0, sizeof(seskey));
345 ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
346 seskeysched, DES_ENCRYPT);
347 memset(&seskeysched, 0, sizeof(seskeysched));
349 /* test against what the client sent */
350 if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
351 memset(randbuf, 0, sizeof(randbuf));
352 return AFPERR_NOTAUTH;
355 memset(randbuf, 0, sizeof(randbuf));
361 /* differences from randnum:
362 * 1) each byte of the key is shifted left one bit
363 * 2) client sends the server a 64-bit number. the server encrypts it
364 * and sends it back as part of the reply.
366 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
367 char *ibuf, size_t ibuflen _U_,
368 char *rbuf, size_t *rbuflen)
375 /* compare session id */
376 memcpy(&sessid, ibuf, sizeof(sessid));
377 if (sessid != randhash(obj))
380 ibuf += sizeof(sessid);
382 /* shift key elements left one bit */
383 for (i = 0; i < sizeof(seskey); i++)
386 /* encrypt randbuf */
387 key_sched((C_Block *) seskey, seskeysched);
388 memset(seskey, 0, sizeof(seskey));
389 ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
390 seskeysched, DES_ENCRYPT);
392 /* test against client's reply */
393 if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
394 memset(randbuf, 0, sizeof(randbuf));
395 memset(&seskeysched, 0, sizeof(seskeysched));
396 return AFPERR_NOTAUTH;
398 ibuf += sizeof(randbuf);
399 memset(randbuf, 0, sizeof(randbuf));
401 /* encrypt client's challenge and send back */
402 ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
403 seskeysched, DES_ENCRYPT);
404 memset(&seskeysched, 0, sizeof(seskeysched));
405 *rbuflen = sizeof(randbuf);
411 /* change password --
412 * NOTE: an FPLogin must already have completed successfully for this
415 static int randnum_changepw(void *obj, const char *username _U_,
416 struct passwd *pwd, char *ibuf,
417 size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen _U_)
423 if (uam_checkuser(pwd) < 0)
424 return AFPERR_ACCESS;
426 len = UAM_PASSWD_FILENAME;
427 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
428 (void *) &passwdfile, &len) < 0)
431 /* old password is encrypted with new password and new password is
432 * encrypted with old. */
433 if ((err = randpass(pwd, passwdfile, seskey,
434 sizeof(seskey), 0)) != AFP_OK)
437 /* use old passwd to decrypt new passwd */
438 key_sched((C_Block *) seskey, seskeysched);
439 ibuf += PASSWDLEN; /* new passwd */
440 ibuf[PASSWDLEN] = '\0';
441 ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
443 /* now use new passwd to decrypt old passwd */
444 key_sched((C_Block *) ibuf, seskeysched);
445 ibuf -= PASSWDLEN; /* old passwd */
446 ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
447 if (memcmp(seskey, ibuf, sizeof(seskey)))
448 err = AFPERR_NOTAUTH;
449 else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
450 err = AFPERR_PWDSAME;
452 else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
453 err = AFPERR_PWDPOLCY;
454 #endif /* USE_CRACKLIB */
457 err = randpass(pwd, passwdfile, (unsigned char *)ibuf + PASSWDLEN, sizeof(seskey), 1);
459 /* zero out some fields */
460 memset(&seskeysched, 0, sizeof(seskeysched));
461 memset(seskey, 0, sizeof(seskey));
462 memset(ibuf, 0, sizeof(seskey)); /* old passwd */
463 memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
472 static int randnum_login(void *obj, struct passwd **uam_pwd,
473 char *ibuf, size_t ibuflen,
474 char *rbuf, size_t *rbuflen)
481 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
482 (void *) &username, &ulen) < 0)
486 return( AFPERR_PARAM );
489 len = (unsigned char) *ibuf++;
491 if (!len || len > ibuflen || len > ulen ) {
492 return( AFPERR_PARAM );
494 memcpy(username, ibuf, len );
497 username[ len ] = '\0';
499 if ((unsigned long) ibuf & 1) { /* pad character */
503 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
506 /* randnum login ext */
507 static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
508 char *ibuf, size_t ibuflen,
509 char *rbuf, size_t *rbuflen)
517 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
518 (void *) &username, &ulen) < 0)
524 memcpy(&temp16, uname, sizeof(temp16));
526 if (!len || len > ulen ) {
527 return( AFPERR_PARAM );
529 memcpy(username, uname +2, len );
530 username[ len ] = '\0';
531 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
534 static int uam_setup(const char *path)
536 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange",
537 randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
540 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange",
541 randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) {
542 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
546 if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange",
547 randnum_changepw) < 0) {
548 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
549 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
552 /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
558 static void uam_cleanup(void)
560 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
561 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
562 uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
563 /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
566 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
569 uam_setup, uam_cleanup