2 * $Id: uams_randnum.c,v 1.15 2003-06-11 07:23:24 srittau 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>
52 #endif /* USE_CRACKLIB */
58 #endif /* __inline__ */
66 static u_int8_t seskey[DES_KEY_SZ];
67 static struct passwd *randpwd;
68 static u_int8_t randbuf[8];
70 /* hash to a 16-bit number. this will generate completely harmless
71 * warnings on 64-bit machines. */
72 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
73 ((unsigned long)a)) & 0xffff)
76 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
77 static __inline__ int home_passwd(const struct passwd *pwd,
78 const char *path, const int pathlen,
79 char *passwd, const int len,
85 if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
86 LOG(log_error, logtype_uams, "Failed to open %s", path);
90 if ( fstat( fd, &st ) < 0 )
91 goto home_passwd_fail;
93 /* If any of these are true, disallow login:
94 * - not a regular file
95 * - gid or uid don't match user
96 * - anyone else has permissions of any sort
98 if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
99 (pwd->pw_gid != st.st_gid) ||
100 (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
101 LOG(log_info, logtype_uams, "Insecure permissions found for %s.", path);
102 goto home_passwd_fail;
105 /* get the password */
107 if (write(fd, passwd, len) < 0) {
108 LOG(log_error, logtype_uams, "Failed to write to %s", path );
109 goto home_passwd_fail;
112 if (read(fd, passwd, len) < 0) {
113 LOG(log_error, logtype_uams, "Failed to read from %s", path );
114 goto home_passwd_fail;
117 /* get rid of pesky characters */
118 for (i = 0; i < len; i++)
119 if ((passwd[i] != ' ') && isspace(passwd[i]))
128 return AFPERR_ACCESS;
134 * handle /path/afppasswd with an optional key file. we're a lot more
135 * trusting of this file. NOTE: we use our own password entry writing
136 * bits as we want to avoid tromping over global variables. in addition,
137 * we look for a key file and use that if it's there. here are the
140 * username:password:last login date:failedcount
142 * password is just the hex equivalent of either the ASCII password
143 * (if the key file doesn't exist) or the des encrypted password.
147 #define PASSWD_ILLEGAL '*'
148 static int afppasswd(const struct passwd *pwd,
149 const char *path, const int pathlen,
150 char *passwd, int len,
153 u_int8_t key[DES_KEY_SZ*2];
154 char buf[MAXPATHLEN + 1], *p;
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 < 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 atalk_unhexify(p, sizeof(key), p, sizeof(key));
198 /* read in the hex representation of an 8-byte key */
199 read(keyfd, key, sizeof(key));
201 /* convert to binary key */
202 len = strlen((char *) key);
203 atalk_unhexify(key, len, key, len);
206 /* NOTE: this takes advantage of the fact that passwd doesn't
207 * get used after this call if it's being set. */
208 err = atalk_encrypt(key, passwd, passwd);
210 err = atalk_decrypt(key, p, p);
212 memset(key, 0, sizeof(key));
222 /* convert to hex password */
223 atalk_hexify(key, sizeof(key), passwd, DES_KEY_SZ);
224 memcpy(p, key, sizeof(key));
226 /* get exclusive access to the user's password entry. we don't
227 * worry so much on reads. in the worse possible case there, the
228 * user will just need to re-enter their password. */
229 lock.l_type = F_WRLCK;
232 lock.l_whence = SEEK_SET;
234 fseek(fp, pos, SEEK_SET);
235 fcntl(fd, F_SETLKW, &lock);
236 fwrite(buf, p - buf + sizeof(key), 1, fp);
237 lock.l_type = F_UNLCK;
238 fcntl(fd, F_SETLK, &lock);
240 memcpy(passwd, p, len);
242 memset(buf, 0, sizeof(buf));
252 /* this sets the uid. it needs to do slightly different things
253 * depending upon whether or not the password is in ~/.passwd
254 * or in a global location */
255 static int randpass(const struct passwd *pwd, const char *file,
256 char *passwd, const int len, const int set)
259 uid_t uid = geteuid();
261 /* Build pathname to user's '.passwd' file */
264 char path[MAXPATHLEN + 1];
266 if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
269 strcpy(path, pwd->pw_dir );
271 strcat(path, file + 2);
273 seteuid(pwd->pw_uid); /* change ourselves to the user */
274 i = home_passwd(pwd, path, i, passwd, len, set);
276 seteuid(0); /* change ourselves back to root */
283 /* handle afppasswd file. we need to make sure that we're root
284 * when we do this. */
287 i = afppasswd(pwd, file, i, passwd, len, set);
294 /* randnum sends an 8-byte number and uses the user's password to
295 * check against the encrypted reply. */
296 static int randnum_login(void *obj, struct passwd **uam_pwd,
297 char *ibuf, int ibuflen,
298 char *rbuf, int *rbuflen)
300 char *username, *passwdfile;
306 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
307 (void *) &username, &ulen) < 0)
310 len = UAM_PASSWD_FILENAME;
311 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
312 (void *) &passwdfile, &len) < 0)
315 len = (unsigned char) *ibuf++;
319 memcpy(username, ibuf, len );
321 username[ len ] = '\0';
322 if ((unsigned long) ibuf & 1) /* padding */
325 if (( randpwd = uam_getname(username, ulen)) == NULL )
326 return AFPERR_PARAM; /* unknown user */
328 LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
329 if (uam_checkuser(randpwd) < 0)
330 return AFPERR_NOTAUTH;
332 if ((err = randpass(randpwd, passwdfile, seskey,
333 sizeof(seskey), 0)) != AFP_OK)
336 /* get a random number */
337 len = sizeof(randbuf);
338 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
339 (void *) randbuf, &len) < 0)
342 /* session id is a hashed version of the obj pointer */
343 sessid = randhash(obj);
344 memcpy(rbuf, &sessid, sizeof(sessid));
345 rbuf += sizeof(sessid);
346 *rbuflen = sizeof(sessid);
348 /* send the random number off */
349 memcpy(rbuf, randbuf, sizeof(randbuf));
350 *rbuflen += sizeof(randbuf);
351 return AFPERR_AUTHCONT;
355 /* check encrypted reply. we actually setup the encryption stuff
356 * here as the first part of randnum and rand2num are identical. */
357 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
358 char *ibuf, int ibuflen,
359 char *rbuf, int *rbuflen)
366 memcpy(&sessid, ibuf, sizeof(sessid));
367 if (sessid != randhash(obj))
370 ibuf += sizeof(sessid);
372 err = atalk_encrypt(seskey, randbuf, randbuf);
373 memset(seskey, 0, sizeof(seskey));
377 /* test against what the client sent */
378 if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
379 memset(randbuf, 0, sizeof(randbuf));
380 return AFPERR_NOTAUTH;
383 memset(randbuf, 0, sizeof(randbuf));
389 /* differences from randnum:
390 * 1) each byte of the key is shifted left one bit
391 * 2) client sends the server a 64-bit number. the server encrypts it
392 * and sends it back as part of the reply.
394 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
395 char *ibuf, int ibuflen,
396 char *rbuf, int *rbuflen)
399 CryptHandle crypt_handle;
405 /* compare session id */
406 memcpy(&sessid, ibuf, sizeof(sessid));
407 if (sessid != randhash(obj))
410 ibuf += sizeof(sessid);
412 /* shift key elements left one bit */
413 for (i = 0; i < sizeof(seskey); i++)
416 /* encrypt randbuf */
417 err = atalk_encrypt_start(&crypt_handle, seskey);
418 atalk_encrypt_do(crypt_handle, randbuf, randbuf);
420 /* test against client's reply */
421 if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
422 memset(randbuf, 0, sizeof(randbuf));
423 return AFPERR_NOTAUTH;
425 ibuf += sizeof(randbuf);
426 memset(randbuf, 0, sizeof(randbuf));
428 /* encrypt client's challenge and send back */
429 atalk_encrypt_do(crypt_handle, rbuf, ibuf);
430 atalk_encrypt_end(crypt_handle);
431 memset(seskey, 0, sizeof(seskey));
433 *rbuflen = sizeof(randbuf);
439 /* change password --
440 * NOTE: an FPLogin must already have completed successfully for this
443 static int randnum_changepw(void *obj, const char *username,
444 struct passwd *pwd, char *ibuf,
445 int ibuflen, char *rbuf, int *rbuflen)
450 if (uam_checkuser(pwd) < 0)
451 return AFPERR_ACCESS;
453 len = UAM_PASSWD_FILENAME;
454 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
455 (void *) &passwdfile, &len) < 0)
458 /* old password is encrypted with new password and new password is
459 * encrypted with old. */
460 if ((err = randpass(pwd, passwdfile, seskey,
461 sizeof(seskey), 0)) != AFP_OK)
464 /* use old passwd to decrypt new passwd */
465 ibuf += PASSWDLEN; /* new passwd */
466 ibuf[PASSWDLEN] = '\0';
467 err = atalk_decrypt(seskey, ibuf, ibuf);
471 /* now use new passwd to decrypt old passwd */
472 ibuf -= PASSWDLEN; /* old passwd */
473 err = atalk_decrypt(ibuf, ibuf, ibuf);
476 if (memcmp(seskey, ibuf, sizeof(seskey)))
477 err = AFPERR_NOTAUTH;
478 else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
479 err = AFPERR_PWDSAME;
481 else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
482 err = AFPERR_PWDPOLCY;
483 #endif /* USE_CRACKLIB */
486 err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
488 /* zero out some fields */
489 memset(seskey, 0, sizeof(seskey));
490 memset(ibuf, 0, sizeof(seskey)); /* old passwd */
491 memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
499 static int uam_setup(const char *path)
501 if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange",
502 randnum_login, randnum_logincont, NULL) < 0)
504 if (uam_register(UAM_SERVER_LOGIN, path, "2-Way Randnum exchange",
505 randnum_login, rand2num_logincont, NULL) < 0) {
506 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
510 if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange",
511 randnum_changepw) < 0) {
512 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
513 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
516 /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
522 static void uam_cleanup(void)
524 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
525 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
526 uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
527 /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
530 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
533 uam_setup, uam_cleanup