2 * $Id: uams_randnum.c,v 1.16 2005-04-28 20:49:50 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 _U_,
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 ( strlen(pwd->pw_name) == (p - buf) &&
176 strncmp(buf, pwd->pw_name, p - buf) == 0) {
178 if (*p == PASSWD_ILLEGAL) {
179 LOG(log_info, logtype_uams, "invalid password entry for %s", pwd->pw_name);
183 goto afppasswd_found;
187 memset(buf, 0, sizeof(buf));
194 /* convert to binary. */
195 for (i = j = 0; i < sizeof(key); i += 2, j++)
196 p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
198 memset(p + j, 0, sizeof(key) - j);
202 /* read in the hex representation of an 8-byte key */
203 read(keyfd, key, sizeof(key));
205 /* convert to binary key */
206 for (i = j = 0; i < strlen((char *) key); i += 2, j++)
207 key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
209 memset(key + j, 0, sizeof(key) - j);
210 key_sched((C_Block *) key, schedule);
211 memset(key, 0, sizeof(key));
214 /* NOTE: this takes advantage of the fact that passwd doesn't
215 * get used after this call if it's being set. */
216 ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
219 /* decrypt the password */
220 ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
222 memset(&schedule, 0, sizeof(schedule));
226 const unsigned char hextable[] = "0123456789ABCDEF";
230 /* convert to hex password */
231 for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
232 key[j] = hextable[(passwd[i] & 0xF0) >> 4];
233 key[j + 1] = hextable[passwd[i] & 0x0F];
235 memcpy(p, key, sizeof(key));
237 /* get exclusive access to the user's password entry. we don't
238 * worry so much on reads. in the worse possible case there, the
239 * user will just need to re-enter their password. */
240 lock.l_type = F_WRLCK;
243 lock.l_whence = SEEK_SET;
245 fseek(fp, pos, SEEK_SET);
246 fcntl(fd, F_SETLKW, &lock);
247 fwrite(buf, p - buf + sizeof(key), 1, fp);
248 lock.l_type = F_UNLCK;
249 fcntl(fd, F_SETLK, &lock);
251 memcpy(passwd, p, len);
253 memset(buf, 0, sizeof(buf));
263 /* this sets the uid. it needs to do slightly different things
264 * depending upon whether or not the password is in ~/.passwd
265 * or in a global location */
266 static int randpass(const struct passwd *pwd, const char *file,
267 char *passwd, const int len, const int set)
270 uid_t uid = geteuid();
272 /* Build pathname to user's '.passwd' file */
275 char path[MAXPATHLEN + 1];
277 if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
280 strcpy(path, pwd->pw_dir );
282 strcat(path, file + 2);
284 seteuid(pwd->pw_uid); /* change ourselves to the user */
285 i = home_passwd(pwd, path, i, passwd, len, set);
287 seteuid(0); /* change ourselves back to root */
294 /* handle afppasswd file. we need to make sure that we're root
295 * when we do this. */
298 i = afppasswd(pwd, file, i, passwd, len, set);
304 /* randnum sends an 8-byte number and uses the user's password to
305 * check against the encrypted reply. */
306 static int rand_login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
307 char *ibuf _U_, int ibuflen _U_,
308 char *rbuf, int *rbuflen)
315 if (( randpwd = uam_getname(obj, username, ulen)) == NULL )
316 return AFPERR_PARAM; /* unknown user */
318 LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
319 if (uam_checkuser(randpwd) < 0)
320 return AFPERR_NOTAUTH;
322 len = UAM_PASSWD_FILENAME;
323 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
324 (void *) &passwdfile, &len) < 0)
327 if ((err = randpass(randpwd, passwdfile, seskey,
328 sizeof(seskey), 0)) != AFP_OK)
331 /* get a random number */
332 len = sizeof(randbuf);
333 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
334 (void *) randbuf, &len) < 0)
337 /* session id is a hashed version of the obj pointer */
338 sessid = randhash(obj);
339 memcpy(rbuf, &sessid, sizeof(sessid));
340 rbuf += sizeof(sessid);
341 *rbuflen = sizeof(sessid);
343 /* send the random number off */
344 memcpy(rbuf, randbuf, sizeof(randbuf));
345 *rbuflen += sizeof(randbuf);
346 return AFPERR_AUTHCONT;
350 /* check encrypted reply. we actually setup the encryption stuff
351 * here as the first part of randnum and rand2num are identical. */
352 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
353 char *ibuf, int ibuflen _U_,
354 char *rbuf _U_, int *rbuflen)
360 memcpy(&sessid, ibuf, sizeof(sessid));
361 if (sessid != randhash(obj))
364 ibuf += sizeof(sessid);
366 /* encrypt. this saves a little space by using the fact that
367 * des can encrypt in-place without side-effects. */
368 key_sched((C_Block *) seskey, seskeysched);
369 memset(seskey, 0, sizeof(seskey));
370 ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
371 seskeysched, DES_ENCRYPT);
372 memset(&seskeysched, 0, sizeof(seskeysched));
374 /* test against what the client sent */
375 if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
376 memset(randbuf, 0, sizeof(randbuf));
377 return AFPERR_NOTAUTH;
380 memset(randbuf, 0, sizeof(randbuf));
386 /* differences from randnum:
387 * 1) each byte of the key is shifted left one bit
388 * 2) client sends the server a 64-bit number. the server encrypts it
389 * and sends it back as part of the reply.
391 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
392 char *ibuf, int ibuflen _U_,
393 char *rbuf, int *rbuflen)
400 /* compare session id */
401 memcpy(&sessid, ibuf, sizeof(sessid));
402 if (sessid != randhash(obj))
405 ibuf += sizeof(sessid);
407 /* shift key elements left one bit */
408 for (i = 0; i < sizeof(seskey); i++)
411 /* encrypt randbuf */
412 key_sched((C_Block *) seskey, seskeysched);
413 memset(seskey, 0, sizeof(seskey));
414 ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
415 seskeysched, DES_ENCRYPT);
417 /* test against client's reply */
418 if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
419 memset(randbuf, 0, sizeof(randbuf));
420 memset(&seskeysched, 0, sizeof(seskeysched));
421 return AFPERR_NOTAUTH;
423 ibuf += sizeof(randbuf);
424 memset(randbuf, 0, sizeof(randbuf));
426 /* encrypt client's challenge and send back */
427 ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
428 seskeysched, DES_ENCRYPT);
429 memset(&seskeysched, 0, sizeof(seskeysched));
430 *rbuflen = sizeof(randbuf);
436 /* change password --
437 * NOTE: an FPLogin must already have completed successfully for this
440 static int randnum_changepw(void *obj, const char *username _U_,
441 struct passwd *pwd, char *ibuf,
442 int ibuflen _U_, char *rbuf _U_, int *rbuflen _U_)
447 if (uam_checkuser(pwd) < 0)
448 return AFPERR_ACCESS;
450 len = UAM_PASSWD_FILENAME;
451 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
452 (void *) &passwdfile, &len) < 0)
455 /* old password is encrypted with new password and new password is
456 * encrypted with old. */
457 if ((err = randpass(pwd, passwdfile, seskey,
458 sizeof(seskey), 0)) != AFP_OK)
461 /* use old passwd to decrypt new passwd */
462 key_sched((C_Block *) seskey, seskeysched);
463 ibuf += PASSWDLEN; /* new passwd */
464 ibuf[PASSWDLEN] = '\0';
465 ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
467 /* now use new passwd to decrypt old passwd */
468 key_sched((C_Block *) ibuf, seskeysched);
469 ibuf -= PASSWDLEN; /* old passwd */
470 ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
471 if (memcmp(seskey, ibuf, sizeof(seskey)))
472 err = AFPERR_NOTAUTH;
473 else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
474 err = AFPERR_PWDSAME;
476 else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
477 err = AFPERR_PWDPOLCY;
478 #endif /* USE_CRACKLIB */
481 err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
483 /* zero out some fields */
484 memset(&seskeysched, 0, sizeof(seskeysched));
485 memset(seskey, 0, sizeof(seskey));
486 memset(ibuf, 0, sizeof(seskey)); /* old passwd */
487 memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
496 static int randnum_login(void *obj, struct passwd **uam_pwd,
497 char *ibuf, int ibuflen,
498 char *rbuf, int *rbuflen)
505 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
506 (void *) &username, &ulen) < 0)
510 return( AFPERR_PARAM );
513 len = (unsigned char) *ibuf++;
515 if (!len || len > ibuflen || len > ulen ) {
516 return( AFPERR_PARAM );
518 memcpy(username, ibuf, len );
521 username[ len ] = '\0';
523 if ((unsigned long) ibuf & 1) { /* pad character */
527 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
530 /* randnum login ext */
531 static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
532 char *ibuf, int ibuflen,
533 char *rbuf, int *rbuflen)
541 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
542 (void *) &username, &ulen) < 0)
548 memcpy(&temp16, uname, sizeof(temp16));
550 if (!len || len > ulen ) {
551 return( AFPERR_PARAM );
553 memcpy(username, uname +2, len );
554 username[ len ] = '\0';
555 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
558 static int uam_setup(const char *path)
560 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange",
561 randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
564 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange",
565 randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) {
566 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
570 if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange",
571 randnum_changepw) < 0) {
572 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
573 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
576 /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
582 static void uam_cleanup(void)
584 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
585 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
586 uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
587 /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
590 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
593 uam_setup, uam_cleanup