]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_randnum.c
AC_HEADER_STDC autoconf change
[netatalk.git] / etc / uams / uams_randnum.c
1 /* 
2  * $Id: uams_randnum.c,v 1.6 2001-09-06 20:00:59 rufustfirefly Exp $
3  *
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.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif /* HAVE_CONFIG_H */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15
16 /* STDC check */
17 #if STDC_HEADERS
18 #include <string.h>
19 #else /* STDC_HEADERS */
20 #ifndef HAVE_STRCHR
21 #define strchr index
22 #define strrchr index
23 #endif /* HAVE_STRCHR */
24 char *strchr (), *strrchr ();
25 #ifndef HAVE_MEMCPY
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 */
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif /* HAVE_UNISTD_H */
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif /* HAVE_FCNTL_H */
37 #include <ctype.h>
38 #include <pwd.h>
39 #include <sys/stat.h>
40 #include <sys/param.h>
41
42 #include <syslog.h>
43
44 #include <netatalk/endian.h>
45
46 #include <atalk/afp.h>
47 #include <atalk/uam.h>
48
49
50 #ifdef UAM_RNDNUM
51 #include <des.h>
52
53 #ifdef USE_CRACKLIB
54 #include <crack.h>
55 #endif /* USE_CRACKLIB */
56
57 #ifndef __inline__
58 #define __inline__
59 #endif /* __inline__ */
60
61 #define PASSWDLEN 8
62
63 static C_Block          seskey;
64 static Key_schedule     seskeysched;
65 static struct passwd    *randpwd;
66 static u_int8_t         randbuf[8];
67
68 /* hash to a 16-bit number. this will generate completely harmless 
69  * warnings on 64-bit machines. */
70 #define randhash(a) (((((unsigned long) a) >> 8) ^ \
71                       ((unsigned long)a)) & 0xffff)
72
73
74 /* handle ~/.passwd. courtesy of shirsch@ibm.net. */
75 static  __inline__ int home_passwd(const struct passwd *pwd, 
76                                    const char *path, const int pathlen, 
77                                    char *passwd, const int len,
78                                    const int set)
79 {
80   struct stat st;
81   int fd, i;
82   
83   if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
84     syslog( LOG_ERR, "Failed to open %s", path);
85     return AFPERR_ACCESS;
86   }
87
88   if ( fstat( fd, &st ) < 0 ) 
89     goto home_passwd_fail;
90   
91   /* If any of these are true, disallow login: 
92    * - not a regular file
93    * - gid or uid don't match user
94    * - anyone else has permissions of any sort
95    */
96   if (!S_ISREG(st.st_mode) || (pwd->pw_uid != st.st_uid) ||
97       (pwd->pw_gid != st.st_gid) ||
98       (st.st_mode & ( S_IRWXG | S_IRWXO )) ) {
99     syslog( LOG_INFO, "Insecure permissions found for %s.", path);
100     goto home_passwd_fail;
101   }
102
103   /* get the password */
104   if (set) {
105     if (write(fd, passwd, len) < 0) {
106       syslog( LOG_ERR, "Failed to write to %s", path );
107       goto home_passwd_fail;
108     }
109   } else {
110     if (read(fd, passwd, len) < 0) {
111       syslog( LOG_ERR, "Failed to read from %s", path );
112       goto home_passwd_fail;
113     }
114   
115   /* get rid of pesky characters */
116   for (i = 0; i < len; i++)
117     if ((passwd[i] != ' ') && isspace(passwd[i]))
118       passwd[i] = '\0';
119   }
120
121   close(fd);
122   return AFP_OK;
123
124 home_passwd_fail:
125   close(fd);
126   return AFPERR_ACCESS;
127 }
128
129
130
131 /* 
132  * handle /path/afppasswd with an optional key file. we're a lot more
133  * trusting of this file. NOTE: we use our own password entry writing
134  * bits as we want to avoid tromping over global variables. in addition,
135  * we look for a key file and use that if it's there. here are the 
136  * formats: 
137  * password file:
138  * username:password:last login date:failedcount
139  *
140  * password is just the hex equivalent of either the ASCII password
141  * (if the key file doesn't exist) or the des encrypted password.
142  *
143  * key file: 
144  * key (in hex) */
145 #define PASSWD_ILLEGAL '*'
146 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
147 static int afppasswd(const struct passwd *pwd, 
148                      const char *path, const int pathlen, 
149                      char *passwd, int len, 
150                      const int set)
151 {
152   u_int8_t key[DES_KEY_SZ*2];
153   char buf[MAXPATHLEN + 1], *p;
154   Key_schedule  schedule;
155   FILE *fp;
156   int i, j, keyfd = -1, err = 0;
157   off_t pos;
158   
159   if ((fp = fopen(path, (set) ? "r+" : "r")) <= 0) {
160     syslog( LOG_ERR, "Failed to open %s", path);
161     return AFPERR_ACCESS;
162   }
163   
164   /* open the key file if it exists */
165   strcpy(buf, path);
166   if (pathlen < sizeof(buf) - 5) {
167     strcat(buf, ".key");
168     keyfd = open(buf, O_RDONLY);
169   } 
170   
171   pos = ftell(fp);
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) {
176         p++;
177         if (*p == PASSWD_ILLEGAL) {
178           syslog(LOG_INFO, "invalid password entry for %s", pwd->pw_name);
179           err = AFPERR_ACCESS;
180           goto afppasswd_done;
181         }
182         goto afppasswd_found;
183       }
184     }
185     pos = ftell(fp);
186     memset(buf, 0, sizeof(buf));
187   }
188   err = AFPERR_PARAM;
189   goto afppasswd_done;
190
191 afppasswd_found:
192   if (!set) {
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]);
196     if (j <= DES_KEY_SZ)
197       memset(p + j, 0, sizeof(key) - j);
198   }
199
200   if (keyfd > -1) {
201       /* read in the hex representation of an 8-byte key */
202       read(keyfd, key, sizeof(key));
203
204       /* convert to binary key */
205       for (i = j = 0; i < strlen(key); i += 2, j++)
206         key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
207       if (j <= DES_KEY_SZ)
208         memset(key + j, 0, sizeof(key) - j);
209       key_sched((C_Block *) key, schedule);
210       memset(key, 0, sizeof(key));
211
212       if (set) {
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,
216                     DES_ENCRYPT);
217       } else {
218         /* decrypt the password */
219         ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
220       }
221       memset(schedule, 0, sizeof(schedule));
222   }
223
224   if (set) {
225     const unsigned char hextable[] = "0123456789ABCDEF";
226     struct flock lock;
227     int fd = fileno(fp);
228
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];
233     }
234     memcpy(p, key, sizeof(key));
235
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;
240     lock.l_start = pos;
241     lock.l_len = 1;
242     lock.l_whence = SEEK_SET;
243
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);
249   } else 
250     memcpy(passwd, p, len);
251
252   memset(buf, 0, sizeof(buf));
253
254 afppasswd_done:
255   if (keyfd > -1)
256     close(keyfd);
257   fclose(fp);
258   return err;
259 }
260
261
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) 
267 {
268   int i;
269   uid_t uid = geteuid();
270
271   /* Build pathname to user's '.passwd' file */
272   i = strlen(file);
273   if (*file == '~') {
274     char path[MAXPATHLEN + 1];
275
276     if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
277     return AFPERR_PARAM;
278   
279     strcpy(path,  pwd->pw_dir );
280     strcat(path, "/" );
281     strcat(path, file + 2);
282     if (!uid)
283       seteuid(pwd->pw_uid); /* change ourselves to the user */
284     i = home_passwd(pwd, path, i, passwd, len, set);
285     if (!uid)
286       seteuid(0); /* change ourselves back to root */
287     return i;
288   } 
289
290   if (i > MAXPATHLEN)
291     return AFPERR_PARAM;
292
293   /* handle afppasswd file. we need to make sure that we're root
294    * when we do this. */
295   if (uid)
296     seteuid(0);
297   i = afppasswd(pwd, file, i, passwd, len, set);
298   if (uid)
299     seteuid(uid);
300   return i;
301 }
302
303   
304 /* randnum sends an 8-byte number and uses the user's password to
305  * check against the encrypted reply. */
306 static int randnum_login(void *obj, struct passwd **uam_pwd,
307                          char *ibuf, int ibuflen,
308                          char *rbuf, int *rbuflen)
309 {
310   char *username, *passwdfile;
311   u_int16_t sessid;
312   int len, ulen, err;
313   
314   *rbuflen = 0;
315   
316   if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 
317                            (void *) &username, &ulen) < 0)
318     return AFPERR_PARAM;
319
320   len = UAM_PASSWD_FILENAME;
321   if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
322                              (void *) &passwdfile, &len) < 0)
323     return AFPERR_PARAM;
324
325   len = (unsigned char) *ibuf++;
326   if ( len > ulen ) {
327         return AFPERR_PARAM;
328   }
329   memcpy(username, ibuf, len );
330   ibuf += len;
331   username[ len ] = '\0';
332   if ((unsigned long) ibuf & 1) /* padding */
333     ++ibuf;
334   
335   if (( randpwd = uam_getname(username, ulen)) == NULL )
336     return AFPERR_PARAM; /* unknown user */
337   
338   syslog( LOG_INFO, "randnum/rand2num login: %s", username);
339   if (uam_checkuser(randpwd) < 0)
340     return AFPERR_NOTAUTH;
341
342   if ((err = randpass(randpwd, passwdfile, seskey,
343                       sizeof(seskey), 0)) != AFP_OK)
344     return err;
345
346   /* get a random number */
347   len = sizeof(randbuf);
348   if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
349                            (void *) randbuf, &len) < 0)
350     return AFPERR_PARAM;
351
352   /* session id is a hashed version of the obj pointer */
353   sessid = randhash(obj);
354   memcpy(rbuf, &sessid, sizeof(sessid));
355   rbuf += sizeof(sessid);
356   *rbuflen = sizeof(sessid);
357   
358   /* send the random number off */
359   memcpy(rbuf, randbuf, sizeof(randbuf));
360   *rbuflen += sizeof(randbuf);
361   return AFPERR_AUTHCONT;
362 }
363
364
365 /* check encrypted reply. we actually setup the encryption stuff
366  * here as the first part of randnum and rand2num are identical. */
367 static int randnum_logincont(void *obj, struct passwd **uam_pwd,
368                              char *ibuf, int ibuflen, 
369                              char *rbuf, int *rbuflen)
370 {
371   u_int16_t sessid;
372
373   *rbuflen = 0;
374
375   memcpy(&sessid, ibuf, sizeof(sessid));
376   if (sessid != randhash(obj))
377     return AFPERR_PARAM;
378
379   ibuf += sizeof(sessid);
380
381   /* encrypt. this saves a little space by using the fact that
382    * des can encrypt in-place without side-effects. */
383   key_sched((C_Block *) seskey, seskeysched);
384   memset(seskey, 0, sizeof(seskey));
385   ecb_encrypt((C_Block *) randbuf, (C_Block *) randbuf,
386                seskeysched, DES_ENCRYPT);
387   memset(seskeysched, 0, sizeof(seskeysched));
388
389   /* test against what the client sent */
390   if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
391     memset(randbuf, 0, sizeof(randbuf));
392     return AFPERR_NOTAUTH;
393   }
394
395   memset(randbuf, 0, sizeof(randbuf));
396   *uam_pwd = randpwd;
397   return AFP_OK;
398 }
399
400
401 /* differences from randnum:
402  * 1) each byte of the key is shifted left one bit
403  * 2) client sends the server a 64-bit number. the server encrypts it
404  *    and sends it back as part of the reply.
405  */
406 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
407                               char *ibuf, int ibuflen, 
408                               char *rbuf, int *rbuflen)
409 {
410   u_int16_t sessid;
411   int i;
412
413   *rbuflen = 0;
414
415   /* compare session id */
416   memcpy(&sessid, ibuf, sizeof(sessid));
417   if (sessid != randhash(obj))
418     return AFPERR_PARAM;
419
420   ibuf += sizeof(sessid);
421
422   /* shift key elements left one bit */
423   for (i = 0; i < sizeof(seskey); i++)
424     seskey[i] <<= 1;
425
426   /* encrypt randbuf */
427   key_sched((C_Block *) seskey, seskeysched);
428   memset(seskey, 0, sizeof(seskey));
429   ecb_encrypt( (C_Block *) randbuf, (C_Block *) randbuf,
430                seskeysched, DES_ENCRYPT);
431
432   /* test against client's reply */
433   if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
434     memset(randbuf, 0, sizeof(randbuf));
435     memset(seskeysched, 0, sizeof(seskeysched));
436     return AFPERR_NOTAUTH;
437   }
438   ibuf += sizeof(randbuf);
439   memset(randbuf, 0, sizeof(randbuf));
440
441   /* encrypt client's challenge and send back */
442   ecb_encrypt( (C_Block *) ibuf, (C_Block *) rbuf,
443                seskeysched, DES_ENCRYPT);
444   memset(seskeysched, 0, sizeof(seskeysched));
445   *rbuflen = sizeof(randbuf);
446   
447   *uam_pwd = randpwd;
448   return AFP_OK;
449 }
450
451 /* change password  --
452  * NOTE: an FPLogin must already have completed successfully for this
453  *       to work. 
454  */
455 static int randnum_changepw(void *obj, const char *username, 
456                             struct passwd *pwd, char *ibuf,
457                             int ibuflen, char *rbuf, int *rbuflen)
458 {
459     char *passwdfile;
460     int err, len;
461
462     if (uam_checkuser(pwd) < 0)
463       return AFPERR_ACCESS;
464
465     len = UAM_PASSWD_FILENAME;
466     if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
467                              (void *) &passwdfile, &len) < 0)
468       return AFPERR_PARAM;
469
470     /* old password is encrypted with new password and new password is
471      * encrypted with old. */
472     if ((err = randpass(pwd, passwdfile, seskey, 
473                         sizeof(seskey), 0)) != AFP_OK)
474       return err;
475
476     /* use old passwd to decrypt new passwd */
477     key_sched((C_Block *) seskey, seskeysched);
478     ibuf += PASSWDLEN; /* new passwd */
479     ibuf[PASSWDLEN] = '\0';
480     ecb_encrypt( (C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
481
482     /* now use new passwd to decrypt old passwd */
483     key_sched((C_Block *) ibuf, seskeysched);
484     ibuf -= PASSWDLEN; /* old passwd */
485     ecb_encrypt((C_Block *) ibuf, (C_Block *) ibuf, seskeysched, DES_DECRYPT);
486     if (memcmp(seskey, ibuf, sizeof(seskey))) 
487         err = AFPERR_NOTAUTH;
488     else if (memcmp(seskey, ibuf + PASSWDLEN, sizeof(seskey)) == 0)
489         err = AFPERR_PWDSAME;
490 #ifdef USE_CRACKLIB
491     else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
492         err = AFPERR_PWDPOLCY;
493 #endif /* USE_CRACKLIB */
494
495     if (!err) 
496       err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
497
498     /* zero out some fields */
499     memset(seskeysched, 0, sizeof(seskeysched));
500     memset(seskey, 0, sizeof(seskey));
501     memset(ibuf, 0, sizeof(seskey)); /* old passwd */
502     memset(ibuf + PASSWDLEN, 0, sizeof(seskey)); /* new passwd */
503
504     if (err)
505       return err;
506
507   return( AFP_OK );
508 }
509
510 static int uam_setup(const char *path)
511 {
512   if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange", 
513                    randnum_login, randnum_logincont, NULL) < 0)
514     return -1;
515   if (uam_register(UAM_SERVER_LOGIN, path, "2-Way Randnum exchange",
516                    randnum_login, rand2num_logincont, NULL) < 0) {
517     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
518     return -1;
519   }
520     
521   if (uam_register(UAM_SERVER_CHANGEPW, path, "Randnum Exchange", 
522                    randnum_changepw) < 0) {
523     uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
524     uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
525     return -1;
526   }
527   /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
528     pam_printer);*/
529
530   return 0;
531 }
532
533 static void uam_cleanup(void)
534 {
535   uam_unregister(UAM_SERVER_LOGIN, "Randnum exchange");
536   uam_unregister(UAM_SERVER_LOGIN, "2-Way Randnum exchange");
537   uam_unregister(UAM_SERVER_CHANGEPW, "Randnum Exchange");
538   /*uam_unregister(UAM_SERVER_PRINTAUTH, "Randnum Exchange");*/
539 }
540
541 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
542   UAM_MODULE_SERVER,
543   UAM_MODULE_VERSION,
544   uam_setup, uam_cleanup
545 };
546
547 #endif /* UAM_RNDNUM */