2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
4 * All Rights Reserved. See COPYRIGHT.
15 #include <sys/param.h>
19 #include <netatalk/endian.h>
21 #include <atalk/afp.h>
22 #include <atalk/uam.h>
38 static C_Block seskey;
39 static Key_schedule seskeysched;
40 static struct passwd *randpwd;
41 static u_int8_t randbuf[8];
43 /* hash to a 16-bit number. this will generate completely harmless
44 * warnings on 64-bit machines. */
45 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
46 ((unsigned long)a)) & 0xffff)
49 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
50 static __inline__ int home_passwd(const struct passwd *pwd,
51 const char *path, const int pathlen,
52 char *passwd, const int len,
58 if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
59 syslog( LOG_ERR, "Failed to open %s", path);
63 if ( fstat( fd, &st ) < 0 )
64 goto home_passwd_fail;
66 /* If any of these are true, disallow login:
67 * - not a regular file
68 * - gid or uid don't match user
69 * - anyone else has permissions of any sort
71 if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
72 (pwd->pw_gid != st.st_gid) ||
73 (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
74 syslog( LOG_INFO, "Insecure permissions found for %s.", path);
75 goto home_passwd_fail;
78 /* get the password */
80 if (write(fd, passwd, len) < 0) {
81 syslog( LOG_ERR, "Failed to write to %s", path );
82 goto home_passwd_fail;
85 if (read(fd, passwd, len) < 0) {
86 syslog( LOG_ERR, "Failed to read from %s", path );
87 goto home_passwd_fail;
90 /* get rid of pesky characters */
91 for (i = 0; i < len; i++)
92 if ((passwd[i] != ' ') && isspace(passwd[i]))
101 return AFPERR_ACCESS;
107 * handle /path/afppasswd with an optional key file. we're a lot more
108 * trusting of this file. NOTE: we use our own password entry writing
109 * bits as we want to avoid tromping over global variables. in addition,
110 * we look for a key file and use that if it's there. here are the
113 * username:password:last login date:failedcount
115 * password is just the hex equivalent of either the ASCII password
116 * (if the key file doesn't exist) or the des encrypted password.
120 #define PASSWD_ILLEGAL '*'
121 #define unhex(x) (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
122 static int afppasswd(const struct passwd *pwd,
123 const char *path, const int pathlen,
124 char *passwd, int len,
127 u_int8_t key[DES_KEY_SZ*2];
128 char buf[MAXPATHLEN + 1], *p;
129 Key_schedule schedule;
131 int i, j, keyfd = -1, err = 0;
134 if ((fp = fopen(path, (set) ? "r+" : "r")) < 0) {
135 syslog( LOG_ERR, "Failed to open %s", path);
136 return AFPERR_ACCESS;
139 /* open the key file if it exists */
141 if (pathlen < sizeof(buf) - 5) {
143 keyfd = open(buf, O_RDONLY);
147 memset(buf, 0, sizeof(buf));
148 while (fgets(buf, sizeof(buf), fp)) {
149 if ((p = strchr(buf, ':'))) {
150 if (strncmp(buf, pwd->pw_name, p - buf) == 0) {
152 if (*p == PASSWD_ILLEGAL) {
153 syslog(LOG_INFO, "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(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 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);
279 /* randnum sends an 8-byte number and uses the user's password to
280 * check against the encrypted reply. */
281 static int randnum_login(void *obj, struct passwd **uam_pwd,
282 char *ibuf, int ibuflen,
283 char *rbuf, int *rbuflen)
285 char *username, *passwdfile;
291 if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
292 (void *) &username, &ulen) < 0)
295 len = UAM_PASSWD_FILENAME;
296 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
297 (void *) &passwdfile, &len) < 0)
300 len = (unsigned char) *ibuf++;
304 memcpy(username, ibuf, len );
306 username[ len ] = '\0';
307 if ((unsigned long) ibuf & 1) /* padding */
310 if (( randpwd = uam_getname(username, ulen)) == NULL )
311 return AFPERR_PARAM; /* unknown user */
313 syslog( LOG_INFO, "randnum/rand2num login: %s", username);
314 if (uam_checkuser(randpwd) < 0)
315 return AFPERR_NOTAUTH;
317 if ((err = randpass(randpwd, passwdfile, seskey,
318 sizeof(seskey), 0)) != AFP_OK)
321 /* get a random number */
322 len = sizeof(randbuf);
323 if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
324 (void *) randbuf, &len) < 0)
327 /* session id is a hashed version of the obj pointer */
328 sessid = randhash(obj);
329 memcpy(rbuf, &sessid, sizeof(sessid));
330 rbuf += sizeof(sessid);
331 *rbuflen = sizeof(sessid);
333 /* send the random number off */
334 memcpy(rbuf, randbuf, sizeof(randbuf));
335 *rbuflen += sizeof(randbuf);
336 return AFPERR_AUTHCONT;
340 /* check encrypted reply. we actually setup the encryption stuff
341 * here as the first part of randnum and rand2num are identical. */
342 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
343 char *ibuf, int ibuflen,
344 char *rbuf, int *rbuflen)
350 memcpy(&sessid, ibuf, sizeof(sessid));
351 if (sessid != randhash(obj))
354 ibuf += sizeof(sessid);
356 /* encrypt. this saves a little space by using the fact that
357 * des can encrypt in-place without side-effects. */
358 key_sched((C_Block *) seskey, seskeysched);
359 memset(seskey, 0, sizeof(seskey));
360 ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
361 seskeysched, DES_ENCRYPT);
362 memset(seskeysched, 0, sizeof(seskeysched));
364 /* test against what the client sent */
365 if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
366 memset(randbuf, 0, sizeof(randbuf));
367 return AFPERR_NOTAUTH;
370 memset(randbuf, 0, sizeof(randbuf));
376 /* differences from randnum:
377 * 1) each byte of the key is shifted left one bit
378 * 2) client sends the server a 64-bit number. the server encrypts it
379 * and sends it back as part of the reply.
381 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
382 char *ibuf, int ibuflen,
383 char *rbuf, int *rbuflen)
390 /* compare session id */
391 memcpy(&sessid, ibuf, sizeof(sessid));
392 if (sessid != randhash(obj))
395 ibuf += sizeof(sessid);
397 /* shift key elements left one bit */
398 for (i = 0; i < sizeof(seskey); i++)
401 /* encrypt randbuf */
402 key_sched((C_Block *) seskey, seskeysched);
403 memset(seskey, 0, sizeof(seskey));
404 ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
405 seskeysched, DES_ENCRYPT);
407 /* test against client's reply */
408 if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
409 memset(randbuf, 0, sizeof(randbuf));
410 memset(seskeysched, 0, sizeof(seskeysched));
411 return AFPERR_NOTAUTH;
413 ibuf += sizeof(randbuf);
414 memset(randbuf, 0, sizeof(randbuf));
416 /* encrypt client's challenge and send back */
417 ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
418 seskeysched, DES_ENCRYPT);
419 memset(seskeysched, 0, sizeof(seskeysched));
420 *rbuflen = sizeof(randbuf);
426 /* change password --
427 * NOTE: an FPLogin must already have completed successfully for this
430 static int randnum_changepw(void *obj, const char *username,
431 struct passwd *pwd, char *ibuf,
432 int ibuflen, char *rbuf, int *rbuflen)
437 if (uam_checkuser(pwd) < 0)
438 return AFPERR_ACCESS;
440 len = UAM_PASSWD_FILENAME;
441 if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT,
442 (void *) &passwdfile, &len) < 0)
445 /* old password is encrypted with new password and new password is
446 * encrypted with old. */
447 if ((err = randpass(pwd, passwdfile, seskey,
448 sizeof(seskey), 0)) != AFP_OK)
451 /* use old passwd to decrypt new passwd */
452 key_sched((C_Block *) seskey, seskeysched);
453 ibuf += PASSWDLEN; /* new passwd */
454 ibuf[PASSWDLEN] = '\0';
455 ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
457 /* now use new passwd to decrypt old passwd */
458 key_sched((C_Block *) ibuf, seskeysched);
459 ibuf -= PASSWDLEN; /* old passwd */
460 ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
461 if (memcmp(seskey, ibuf, sizeof(seskey)))
462 err = AFPERR_NOTAUTH;
463 else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
464 err = AFPERR_PWDSAME;
466 else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
467 err = AFPERR_PWDPOLCY;
471 err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
473 /* zero out some fields */
474 memset(seskeysched, 0, sizeof(seskeysched));
475 memset(seskey, 0, sizeof(seskey));
476 memset(ibuf, 0, sizeof(seskey)); /* old passwd */
477 memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
485 static int uam_setup(const char *path)
487 if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange",
488 randnum_login, randnum_logincont, NULL) < 0)
490 if (uam_register(UAM_SERVER_LOGIN, path, "2-Way Randnum exchange",
491 randnum_login, rand2num_logincont, NULL) < 0) {
492 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
496 if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange",
497 randnum_changepw) < 0) {
498 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
499 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
502 /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
508 static void uam_cleanup(void)
510 uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
511 uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
512 uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
513 /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
516 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
519 uam_setup, uam_cleanup
522 #endif /* UAM_RNDNUM */