2 * $Id: uams_randnum.c,v 1.21 2010-03-30 10:25:49 franklahm 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 */
18 #endif /* HAVE_UNISTD_H */
21 #endif /* HAVE_FCNTL_H */
25 #include <sys/param.h>
27 #include <atalk/logger.h>
29 #include <netatalk/endian.h>
31 #include <atalk/afp.h>
32 #include <atalk/uam.h>
39 #endif /* USE_CRACKLIB */
43 static C_Block seskey;
44 static Key_schedule seskeysched;
45 static struct passwd *randpwd;
46 static u_int8_t randbuf[8];
48 /* hash to a 16-bit number. this will generate completely harmless
49 * warnings on 64-bit machines. */
50 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
51 ((unsigned long)a)) & 0xffff)
54 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
55 static int home_passwd(const struct passwd *pwd,
56 const char *path, const int pathlen _U_,
57 unsigned char *passwd, const int len,
63 if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
64 LOG(log_error, logtype_uams, "Failed to open %s", path);
68 if ( fstat( fd, &st ) < 0 )
69 goto home_passwd_fail;
71 /* If any of these are true, disallow login:
72 * - not a regular file
73 * - gid or uid don't match user
74 * - anyone else has permissions of any sort
76 if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
77 (pwd->pw_gid != st.st_gid) ||
78 (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
79 LOG(log_info, logtype_uams, "Insecure permissions found for %s.", path);
80 goto home_passwd_fail;
83 /* get the password */
85 if (write(fd, passwd, len) < 0) {
86 LOG(log_error, logtype_uams, "Failed to write to %s", path );
87 goto home_passwd_fail;
90 if (read(fd, passwd, len) < 0) {
91 LOG(log_error, logtype_uams, "Failed to read from %s", path );
92 goto home_passwd_fail;
95 /* get rid of pesky characters */
96 for (i = 0; i < len; i++)
97 if ((passwd[i] != ' ') && isspace(passwd[i]))
106 return AFPERR_ACCESS;
112 * handle /path/afppasswd with an optional key file. we're a lot more
113 * trusting of this file. NOTE: we use our own password entry writing
114 * bits as we want to avoid tromping over global variables. in addition,
115 * we look for a key file and use that if it's there. here are the
118 * username:password:last login date:failedcount
120 * password is just the hex equivalent of either the ASCII password
121 * (if the key file doesn't exist) or the des encrypted password.
125 #define PASSWD_ILLEGAL '*'
126 #define unhex(x) (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
127 static int afppasswd(const struct passwd *pwd,
128 const char *path, const int pathlen,
129 unsigned char *passwd, int len,
132 u_int8_t key[DES_KEY_SZ*2];
133 char buf[MAXPATHLEN + 1], *p;
134 Key_schedule schedule;
137 int keyfd = -1, err = 0;
140 if ((fp = fopen(path, (set) ? "r+" : "r")) == NULL) {
141 LOG(log_error, logtype_uams, "Failed to open %s", path);
142 return AFPERR_ACCESS;
145 /* open the key file if it exists */
147 if (pathlen < (int) sizeof(buf) - 5) {
149 keyfd = open(buf, O_RDONLY);
153 memset(buf, 0, sizeof(buf));
154 while (fgets(buf, sizeof(buf), fp)) {
155 if ((p = strchr(buf, ':'))) {
156 if ( strlen(pwd->pw_name) == (p - buf) &&
157 strncmp(buf, pwd->pw_name, p - buf) == 0) {
159 if (*p == PASSWD_ILLEGAL) {
160 LOG(log_info, logtype_uams, "invalid password entry for %s", pwd->pw_name);
164 goto afppasswd_found;
168 memset(buf, 0, sizeof(buf));
175 /* convert to binary. */
176 for (i = j = 0; i < sizeof(key); i += 2, j++)
177 p[j] = (unhex(p[i]) << 4) | unhex(p[i + 1]);
179 memset(p + j, 0, sizeof(key) - j);
183 /* read in the hex representation of an 8-byte key */
184 read(keyfd, key, sizeof(key));
186 /* convert to binary key */
187 for (i = j = 0; i < strlen((char *) key); i += 2, j++)
188 key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
190 memset(key + j, 0, sizeof(key) - j);
191 key_sched((C_Block *) key, schedule);
192 memset(key, 0, sizeof(key));
195 /* NOTE: this takes advantage of the fact that passwd doesn't
196 * get used after this call if it's being set. */
197 ecb_encrypt((C_Block *) passwd, (C_Block *) passwd, schedule,
200 /* decrypt the password */
201 ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
203 memset(&schedule, 0, sizeof(schedule));
207 const unsigned char hextable[] = "0123456789ABCDEF";
211 /* convert to hex password */
212 for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
213 key[j] = hextable[(passwd[i] & 0xF0) >> 4];
214 key[j + 1] = hextable[passwd[i] & 0x0F];
216 memcpy(p, key, sizeof(key));
218 /* get exclusive access to the user's password entry. we don't
219 * worry so much on reads. in the worse possible case there, the
220 * user will just need to re-enter their password. */
221 lock.l_type = F_WRLCK;
224 lock.l_whence = SEEK_SET;
226 fseek(fp, pos, SEEK_SET);
227 fcntl(fd, F_SETLKW, &lock);
228 fwrite(buf, p - buf + sizeof(key), 1, fp);
229 lock.l_type = F_UNLCK;
230 fcntl(fd, F_SETLK, &lock);
232 memcpy(passwd, p, len);
234 memset(buf, 0, sizeof(buf));
244 /* this sets the uid. it needs to do slightly different things
245 * depending upon whether or not the password is in ~/.passwd
246 * or in a global location */
247 static int randpass(const struct passwd *pwd, const char *file,
248 unsigned char *passwd, const int len, const int set)
251 uid_t uid = geteuid();
253 /* Build pathname to user's '.passwd' file */
256 char path[MAXPATHLEN + 1];
258 if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
261 strcpy(path, pwd->pw_dir );
263 strcat(path, file + 2);
265 seteuid(pwd->pw_uid); /* change ourselves to the user */
266 i = home_passwd(pwd, path, i, passwd, len, set);
268 seteuid(0); /* change ourselves back to root */
275 /* handle afppasswd file. we need to make sure that we're root
276 * when we do this. */
279 i = afppasswd(pwd, file, i, passwd, len, set);
285 /* randnum sends an 8-byte number and uses the user's password to
286 * check against the encrypted reply. */
287 static int rand_login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
288 char *ibuf _U_, size_t ibuflen _U_,
289 char *rbuf, size_t *rbuflen)
297 if (( randpwd = uam_getname(obj, username, ulen)) == NULL )
298 return AFPERR_NOTAUTH; /* unknown user */
300 LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
301 if (uam_checkuser(randpwd) < 0)
302 return AFPERR_NOTAUTH;
304 len = UAM_PASSWD_FILENAME;
305 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
306 (void *) &passwdfile, &len) < 0)
309 if ((err = randpass(randpwd, passwdfile, seskey,
310 sizeof(seskey), 0)) != AFP_OK)
313 /* get a random number */
314 len = sizeof(randbuf);
315 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
316 (void *) randbuf, &len) < 0)
319 /* session id is a hashed version of the obj pointer */
320 sessid = randhash(obj);
321 memcpy(rbuf, &sessid, sizeof(sessid));
322 rbuf += sizeof(sessid);
323 *rbuflen = sizeof(sessid);
325 /* send the random number off */
326 memcpy(rbuf, randbuf, sizeof(randbuf));
327 *rbuflen += sizeof(randbuf);
328 return AFPERR_AUTHCONT;
332 /* check encrypted reply. we actually setup the encryption stuff
333 * here as the first part of randnum and rand2num are identical. */
334 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
335 char *ibuf, size_t ibuflen _U_,
336 char *rbuf _U_, size_t *rbuflen)
342 memcpy(&sessid, ibuf, sizeof(sessid));
343 if (sessid != randhash(obj))
346 ibuf += sizeof(sessid);
348 /* encrypt. this saves a little space by using the fact that
349 * des can encrypt in-place without side-effects. */
350 key_sched((C_Block *) seskey, seskeysched);
351 memset(seskey, 0, sizeof(seskey));
352 ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
353 seskeysched, DES_ENCRYPT);
354 memset(&seskeysched, 0, sizeof(seskeysched));
356 /* test against what the client sent */
357 if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
358 memset(randbuf, 0, sizeof(randbuf));
359 return AFPERR_NOTAUTH;
362 memset(randbuf, 0, sizeof(randbuf));
368 /* differences from randnum:
369 * 1) each byte of the key is shifted left one bit
370 * 2) client sends the server a 64-bit number. the server encrypts it
371 * and sends it back as part of the reply.
373 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
374 char *ibuf, size_t ibuflen _U_,
375 char *rbuf, size_t *rbuflen)
382 /* compare session id */
383 memcpy(&sessid, ibuf, sizeof(sessid));
384 if (sessid != randhash(obj))
387 ibuf += sizeof(sessid);
389 /* shift key elements left one bit */
390 for (i = 0; i < sizeof(seskey); i++)
393 /* encrypt randbuf */
394 key_sched((C_Block *) seskey, seskeysched);
395 memset(seskey, 0, sizeof(seskey));
396 ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
397 seskeysched, DES_ENCRYPT);
399 /* test against client's reply */
400 if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
401 memset(randbuf, 0, sizeof(randbuf));
402 memset(&seskeysched, 0, sizeof(seskeysched));
403 return AFPERR_NOTAUTH;
405 ibuf += sizeof(randbuf);
406 memset(randbuf, 0, sizeof(randbuf));
408 /* encrypt client's challenge and send back */
409 ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
410 seskeysched, DES_ENCRYPT);
411 memset(&seskeysched, 0, sizeof(seskeysched));
412 *rbuflen = sizeof(randbuf);
418 /* change password --
419 * NOTE: an FPLogin must already have completed successfully for this
422 static int randnum_changepw(void *obj, const char *username _U_,
423 struct passwd *pwd, char *ibuf,
424 size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen _U_)
430 if (uam_checkuser(pwd) < 0)
431 return AFPERR_ACCESS;
433 len = UAM_PASSWD_FILENAME;
434 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
435 (void *) &passwdfile, &len) < 0)
438 /* old password is encrypted with new password and new password is
439 * encrypted with old. */
440 if ((err = randpass(pwd, passwdfile, seskey,
441 sizeof(seskey), 0)) != AFP_OK)
444 /* use old passwd to decrypt new passwd */
445 key_sched((C_Block *) seskey, seskeysched);
446 ibuf += PASSWDLEN; /* new passwd */
447 ibuf[PASSWDLEN] = '\0';
448 ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
450 /* now use new passwd to decrypt old passwd */
451 key_sched((C_Block *) ibuf, seskeysched);
452 ibuf -= PASSWDLEN; /* old passwd */
453 ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
454 if (memcmp(seskey, ibuf, sizeof(seskey)))
455 err = AFPERR_NOTAUTH;
456 else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
457 err = AFPERR_PWDSAME;
459 else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
460 err = AFPERR_PWDPOLCY;
461 #endif /* USE_CRACKLIB */
464 err = randpass(pwd, passwdfile, (unsigned char *)ibuf + PASSWDLEN, sizeof(seskey), 1);
466 /* zero out some fields */
467 memset(&seskeysched, 0, sizeof(seskeysched));
468 memset(seskey, 0, sizeof(seskey));
469 memset(ibuf, 0, sizeof(seskey)); /* old passwd */
470 memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
479 static int randnum_login(void *obj, struct passwd **uam_pwd,
480 char *ibuf, size_t ibuflen,
481 char *rbuf, size_t *rbuflen)
488 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
489 (void *) &username, &ulen) < 0)
493 return( AFPERR_PARAM );
496 len = (unsigned char) *ibuf++;
498 if (!len || len > ibuflen || len > ulen ) {
499 return( AFPERR_PARAM );
501 memcpy(username, ibuf, len );
504 username[ len ] = '\0';
506 if ((unsigned long) ibuf & 1) { /* pad character */
510 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
513 /* randnum login ext */
514 static int randnum_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
515 char *ibuf, size_t ibuflen,
516 char *rbuf, size_t *rbuflen)
524 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
525 (void *) &username, &ulen) < 0)
531 memcpy(&temp16, uname, sizeof(temp16));
533 if (!len || len > ulen ) {
534 return( AFPERR_PARAM );
536 memcpy(username, uname +2, len );
537 username[ len ] = '\0';
538 return (rand_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
541 static int uam_setup(const char *path)
543 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "Randnum exchange",
544 randnum_login, randnum_logincont, NULL, randnum_login_ext) < 0)
547 if (uam_register(UAM_SERVER_LOGIN_EXT, path, "2-Way Randnum exchange",
548 randnum_login, rand2num_logincont, NULL, randnum_login_ext) < 0) {
549 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
553 if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange",
554 randnum_changepw) < 0) {
555 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
556 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
559 /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
565 static void uam_cleanup(void)
567 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
568 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
569 uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
570 /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
573 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
576 uam_setup, uam_cleanup