2 * $Id: uams_randnum.c,v 1.12.6.3 2004-02-25 00:37:19 bfernhomberg Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
6 * All Rights Reserved. See COPYRIGHT.
11 #endif /* HAVE_CONFIG_H */
19 #else /* STDC_HEADERS */
23 #endif /* HAVE_STRCHR */
24 char *strchr (), *strrchr ();
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
33 #endif /* HAVE_UNISTD_H */
36 #endif /* HAVE_FCNTL_H */
40 #include <sys/param.h>
42 #include <atalk/logger.h>
44 #include <netatalk/endian.h>
46 #include <atalk/afp.h>
47 #include <atalk/uam.h>
54 #endif /* USE_CRACKLIB */
58 #endif /* __inline__ */
62 static C_Block seskey;
63 static Key_schedule seskeysched;
64 static struct passwd *randpwd;
65 static u_int8_t randbuf[8];
67 /* hash to a 16-bit number. this will generate completely harmless
68 * warnings on 64-bit machines. */
69 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
70 ((unsigned long)a)) & 0xffff)
73 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
74 static __inline__ int home_passwd(const struct passwd *pwd,
75 const char *path, const int pathlen,
76 char *passwd, const int len,
82 if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
83 LOG(log_error, logtype_uams, "Failed to open %s", path);
87 if ( fstat( fd, &st ) < 0 )
88 goto home_passwd_fail;
90 /* If any of these are true, disallow login:
91 * - not a regular file
92 * - gid or uid don't match user
93 * - anyone else has permissions of any sort
95 if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
96 (pwd->pw_gid != st.st_gid) ||
97 (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
98 LOG(log_info, logtype_uams, "Insecure permissions found for %s.", path);
99 goto home_passwd_fail;
102 /* get the password */
104 if (write(fd, passwd, len) < 0) {
105 LOG(log_error, logtype_uams, "Failed to write to %s", path );
106 goto home_passwd_fail;
109 if (read(fd, passwd, len) < 0) {
110 LOG(log_error, logtype_uams, "Failed to read from %s", path );
111 goto home_passwd_fail;
114 /* get rid of pesky characters */
115 for (i = 0; i < len; i++)
116 if ((passwd[i] != ' ') && isspace(passwd[i]))
125 return AFPERR_ACCESS;
131 * handle /path/afppasswd with an optional key file. we're a lot more
132 * trusting of this file. NOTE: we use our own password entry writing
133 * bits as we want to avoid tromping over global variables. in addition,
134 * we look for a key file and use that if it's there. here are the
137 * username:password:last login date:failedcount
139 * password is just the hex equivalent of either the ASCII password
140 * (if the key file doesn't exist) or the des encrypted password.
144 #define PASSWD_ILLEGAL '*'
145 #define unhex(x) (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
146 static int afppasswd(const struct passwd *pwd,
147 const char *path, const int pathlen,
148 char *passwd, int len,
151 u_int8_t key[DES_KEY_SZ*2];
152 char buf[MAXPATHLEN + 1], *p;
153 Key_schedule schedule;
156 int keyfd = -1, err = 0;
159 if ((fp = fopen(path, (set) ? "r+" : "r")) == NULL) {
160 LOG(log_error, logtype_uams, "Failed to open %s", path);
161 return AFPERR_ACCESS;
164 /* open the key file if it exists */
166 if (pathlen < (int) sizeof(buf) - 5) {
168 keyfd = open(buf, O_RDONLY);
172 memset(buf, 0, sizeof(buf));
173 while (fgets(buf, sizeof(buf), fp)) {
174 if ((p = strchr(buf, ':'))) {
175 if (strncmp(buf, pwd->pw_name, p - buf) == 0) {
177 if (*p == PASSWD_ILLEGAL) {
178 LOG(log_info, logtype_uams, "invalid password entry for %s", pwd->pw_name);
182 goto afppasswd_found;
186 memset(buf, 0, sizeof(buf));
193 /* convert to binary. */
194 for (i = j = 0; i < sizeof(key); i += 2, j++)
195 p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
197 memset(p + j, 0, sizeof(key) - j);
201 /* read in the hex representation of an 8-byte key */
202 read(keyfd, key, sizeof(key));
204 /* convert to binary key */
205 for (i = j = 0; i < strlen((char *) key); i += 2, j++)
206 key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
208 memset(key + j, 0, sizeof(key) - j);
209 key_sched((C_Block *) key, schedule);
210 memset(key, 0, sizeof(key));
213 /* NOTE: this takes advantage of the fact that passwd doesn't
214 * get used after this call if it's being set. */
215 ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
218 /* decrypt the password */
219 ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
221 memset(&schedule, 0, sizeof(schedule));
225 const unsigned char hextable[] = "0123456789ABCDEF";
229 /* convert to hex password */
230 for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
231 key[j] = hextable[(passwd[i] & 0xF0) >> 4];
232 key[j + 1] = hextable[passwd[i] & 0x0F];
234 memcpy(p, key, sizeof(key));
236 /* get exclusive access to the user's password entry. we don't
237 * worry so much on reads. in the worse possible case there, the
238 * user will just need to re-enter their password. */
239 lock.l_type = F_WRLCK;
242 lock.l_whence = SEEK_SET;
244 fseek(fp, pos, SEEK_SET);
245 fcntl(fd, F_SETLKW, &lock);
246 fwrite(buf, p - buf + sizeof(key), 1, fp);
247 lock.l_type = F_UNLCK;
248 fcntl(fd, F_SETLK, &lock);
250 memcpy(passwd, p, len);
252 memset(buf, 0, sizeof(buf));
262 /* this sets the uid. it needs to do slightly different things
263 * depending upon whether or not the password is in ~/.passwd
264 * or in a global location */
265 static int randpass(const struct passwd *pwd, const char *file,
266 char *passwd, const int len, const int set)
269 uid_t uid = geteuid();
271 /* Build pathname to user's '.passwd' file */
274 char path[MAXPATHLEN + 1];
276 if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
279 strcpy(path, pwd->pw_dir );
281 strcat(path, file + 2);
283 seteuid(pwd->pw_uid); /* change ourselves to the user */
284 i = home_passwd(pwd, path, i, passwd, len, set);
286 seteuid(0); /* change ourselves back to root */
293 /* handle afppasswd file. we need to make sure that we're root
294 * when we do this. */
297 i = afppasswd(pwd, file, i, passwd, len, set);
303 /* randnum sends an 8-byte number and uses the user's password to
304 * check against the encrypted reply. */
305 static int rand_login(void *obj, char *username, int ulen, struct passwd **uam_pwd,
306 char *ibuf, int ibuflen,
307 char *rbuf, int *rbuflen)
314 if (( randpwd = uam_getname(obj, username, ulen)) == NULL )
315 return AFPERR_PARAM; /* unknown user */
317 LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
318 if (uam_checkuser(randpwd) < 0)
319 return AFPERR_NOTAUTH;
321 len = UAM_PASSWD_FILENAME;
322 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
323 (void *) &passwdfile, &len) < 0)
326 if ((err = randpass(randpwd, passwdfile, seskey,
327 sizeof(seskey), 0)) != AFP_OK)
330 /* get a random number */
331 len = sizeof(randbuf);
332 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
333 (void *) randbuf, &len) < 0)
336 /* session id is a hashed version of the obj pointer */
337 sessid = randhash(obj);
338 memcpy(rbuf, &sessid, sizeof(sessid));
339 rbuf += sizeof(sessid);
340 *rbuflen = sizeof(sessid);
342 /* send the random number off */
343 memcpy(rbuf, randbuf, sizeof(randbuf));
344 *rbuflen += sizeof(randbuf);
345 return AFPERR_AUTHCONT;
349 /* check encrypted reply. we actually setup the encryption stuff
350 * here as the first part of randnum and rand2num are identical. */
351 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
352 char *ibuf, int ibuflen,
353 char *rbuf, int *rbuflen)
359 memcpy(&sessid, ibuf, sizeof(sessid));
360 if (sessid != randhash(obj))
363 ibuf += sizeof(sessid);
365 /* encrypt. this saves a little space by using the fact that
366 * des can encrypt in-place without side-effects. */
367 key_sched((C_Block *) seskey, seskeysched);
368 memset(seskey, 0, sizeof(seskey));
369 ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
370 seskeysched, DES_ENCRYPT);
371 memset(&seskeysched, 0, sizeof(seskeysched));
373 /* test against what the client sent */
374 if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
375 memset(randbuf, 0, sizeof(randbuf));
376 return AFPERR_NOTAUTH;
379 memset(randbuf, 0, sizeof(randbuf));
385 /* differences from randnum:
386 * 1) each byte of the key is shifted left one bit
387 * 2) client sends the server a 64-bit number. the server encrypts it
388 * and sends it back as part of the reply.
390 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
391 char *ibuf, int ibuflen,
392 char *rbuf, int *rbuflen)
399 /* compare session id */
400 memcpy(&sessid, ibuf, sizeof(sessid));
401 if (sessid != randhash(obj))
404 ibuf += sizeof(sessid);
406 /* shift key elements left one bit */
407 for (i = 0; i < sizeof(seskey); i++)
410 /* encrypt randbuf */
411 key_sched((C_Block *) seskey, seskeysched);
412 memset(seskey, 0, sizeof(seskey));
413 ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
414 seskeysched, DES_ENCRYPT);
416 /* test against client's reply */
417 if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
418 memset(randbuf, 0, sizeof(randbuf));
419 memset(&seskeysched, 0, sizeof(seskeysched));
420 return AFPERR_NOTAUTH;
422 ibuf += sizeof(randbuf);
423 memset(randbuf, 0, sizeof(randbuf));
425 /* encrypt client's challenge and send back */
426 ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
427 seskeysched, DES_ENCRYPT);
428 memset(&seskeysched, 0, sizeof(seskeysched));
429 *rbuflen = sizeof(randbuf);
435 /* change password --
436 * NOTE: an FPLogin must already have completed successfully for this
439 static int randnum_changepw(void *obj, const char *username,
440 struct passwd *pwd, char *ibuf,
441 int ibuflen, char *rbuf, int *rbuflen)
446 if (uam_checkuser(pwd) < 0)
447 return AFPERR_ACCESS;
449 len = UAM_PASSWD_FILENAME;
450 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
451 (void *) &passwdfile, &len) < 0)
454 /* old password is encrypted with new password and new password is
455 * encrypted with old. */
456 if ((err = randpass(pwd, passwdfile, seskey,
457 sizeof(seskey), 0)) != AFP_OK)
460 /* use old passwd to decrypt new passwd */
461 key_sched((C_Block *) seskey, seskeysched);
462 ibuf += PASSWDLEN; /* new passwd */
463 ibuf[PASSWDLEN] = '\0';
464 ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
466 /* now use new passwd to decrypt old passwd */
467 key_sched((C_Block *) ibuf, seskeysched);
468 ibuf -= PASSWDLEN; /* old passwd */
469 ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
470 if (memcmp(seskey, ibuf, sizeof(seskey)))
471 err = AFPERR_NOTAUTH;
472 else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
473 err = AFPERR_PWDSAME;
475 else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
476 err = AFPERR_PWDPOLCY;
477 #endif /* USE_CRACKLIB */
480 err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
482 /* zero out some fields */
483 memset(&seskeysched, 0, sizeof(seskeysched));
484 memset(seskey, 0, sizeof(seskey));
485 memset(ibuf, 0, sizeof(seskey)); /* old passwd */
486 memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
495 static int randnum_login(void *obj, struct passwd **uam_pwd,
496 char *ibuf, int ibuflen,
497 char *rbuf, int *rbuflen)
504 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
505 (void *) &username, &ulen) < 0)
509 return( AFPERR_PARAM );
512 len = (unsigned char) *ibuf++;
514 if (!len || len > ibuflen || len > ulen ) {
515 return( AFPERR_PARAM );
517 memcpy(username, ibuf, len );
520 username[ len ] = '\0';
522 if ((unsigned long) ibuf & 1) { /* pad character */
526 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
529 /* randnum login ext */
530 static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
531 char *ibuf, int ibuflen,
532 char *rbuf, int *rbuflen)
540 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
541 (void *) &username, &ulen) < 0)
547 memcpy(&temp16, uname, sizeof(temp16));
549 if (!len || len > ulen ) {
550 return( AFPERR_PARAM );
552 memcpy(username, uname +2, len );
553 username[ len ] = '\0';
554 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
557 static int uam_setup(const char *path)
559 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange",
560 randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
563 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange",
564 randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) {
565 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
569 if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange",
570 randnum_changepw) < 0) {
571 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
572 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
575 /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
581 static void uam_cleanup(void)
583 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
584 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
585 uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
586 /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
589 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
592 uam_setup, uam_cleanup