]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_randnum.c
Removed all traces of OpenSSL specific stuff.
[netatalk.git] / etc / uams / uams_randnum.c
1 /* 
2  * $Id: uams_randnum.c,v 1.15 2003-06-11 07:23:24 srittau 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 <atalk/logger.h>
43
44 #include <netatalk/endian.h>
45
46 #include <atalk/afp.h>
47 #include <atalk/uam.h>
48
49
50 #ifdef USE_CRACKLIB
51 #include <crack.h>
52 #endif /* USE_CRACKLIB */
53
54 #include "crypt.h"
55
56 #ifndef __inline__
57 #define __inline__
58 #endif /* __inline__ */
59
60 #define PASSWDLEN 8
61
62 #ifndef DES_KEY_SZ
63 #define DES_KEY_SZ 8
64 #endif
65
66 static u_int8_t          seskey[DES_KEY_SZ];
67 static struct passwd    *randpwd;
68 static u_int8_t          randbuf[8];
69
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)
74
75
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,
80                                    const int set)
81 {
82   struct stat st;
83   int fd, i;
84   
85   if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
86     LOG(log_error, logtype_uams, "Failed to open %s", path);
87     return AFPERR_ACCESS;
88   }
89
90   if ( fstat( fd, &st ) < 0 ) 
91     goto home_passwd_fail;
92   
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
97    */
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;
103   }
104
105   /* get the password */
106   if (set) {
107     if (write(fd, passwd, len) < 0) {
108       LOG(log_error, logtype_uams, "Failed to write to %s", path );
109       goto home_passwd_fail;
110     }
111   } else {
112     if (read(fd, passwd, len) < 0) {
113       LOG(log_error, logtype_uams, "Failed to read from %s", path );
114       goto home_passwd_fail;
115     }
116   
117   /* get rid of pesky characters */
118   for (i = 0; i < len; i++)
119     if ((passwd[i] != ' ') && isspace(passwd[i]))
120       passwd[i] = '\0';
121   }
122
123   close(fd);
124   return AFP_OK;
125
126 home_passwd_fail:
127   close(fd);
128   return AFPERR_ACCESS;
129 }
130
131
132
133 /* 
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 
138  * formats: 
139  * password file:
140  * username:password:last login date:failedcount
141  *
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.
144  *
145  * key file: 
146  * key (in hex) */
147 #define PASSWD_ILLEGAL '*'
148 static int afppasswd(const struct passwd *pwd, 
149                      const char *path, const int pathlen, 
150                      char *passwd, int len, 
151                      const int set)
152 {
153   u_int8_t key[DES_KEY_SZ*2];
154   char buf[MAXPATHLEN + 1], *p;
155   FILE *fp;
156   int keyfd = -1, err = 0;
157   off_t pos;
158   
159   if ((fp = fopen(path, (set) ? "r+" : "r")) == NULL) {
160     LOG(log_error, logtype_uams, "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           LOG(log_info, logtype_uams, "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     atalk_unhexify(p, sizeof(key), p, sizeof(key));
194
195   if (keyfd > -1) {
196       size_t len;
197
198       /* read in the hex representation of an 8-byte key */
199       read(keyfd, key, sizeof(key));
200
201       /* convert to binary key */
202       len = strlen((char *) key);
203       atalk_unhexify(key, len, key, len);
204
205       if (set) {
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);
209       } else {
210         err = atalk_decrypt(key, p, p);
211       }
212       memset(key, 0, sizeof(key));
213
214       if (err)
215         goto afppasswd_done;
216   }
217
218   if (set) {
219     struct flock lock;
220     int fd = fileno(fp);
221
222     /* convert to hex password */
223     atalk_hexify(key, sizeof(key), passwd, DES_KEY_SZ);
224     memcpy(p, key, sizeof(key));
225
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;
230     lock.l_start = pos;
231     lock.l_len = 1;
232     lock.l_whence = SEEK_SET;
233
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);
239   } else 
240     memcpy(passwd, p, len);
241
242   memset(buf, 0, sizeof(buf));
243
244 afppasswd_done:
245   if (keyfd > -1)
246     close(keyfd);
247   fclose(fp);
248   return err;
249 }
250
251
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) 
257 {
258   int i;
259   uid_t uid = geteuid();
260
261   /* Build pathname to user's '.passwd' file */
262   i = strlen(file);
263   if (*file == '~') {
264     char path[MAXPATHLEN + 1];
265
266     if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
267     return AFPERR_PARAM;
268   
269     strcpy(path,  pwd->pw_dir );
270     strcat(path, "/" );
271     strcat(path, file + 2);
272     if (!uid)
273       seteuid(pwd->pw_uid); /* change ourselves to the user */
274     i = home_passwd(pwd, path, i, passwd, len, set);
275     if (!uid)
276       seteuid(0); /* change ourselves back to root */
277     return i;
278   } 
279
280   if (i > MAXPATHLEN)
281     return AFPERR_PARAM;
282
283   /* handle afppasswd file. we need to make sure that we're root
284    * when we do this. */
285   if (uid)
286     seteuid(0);
287   i = afppasswd(pwd, file, i, passwd, len, set);
288   if (uid)
289     seteuid(uid);
290   return i;
291 }
292
293   
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)
299 {
300   char *username, *passwdfile;
301   u_int16_t sessid;
302   int len, ulen, err;
303   
304   *rbuflen = 0;
305   
306   if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 
307                            (void *) &username, &ulen) < 0)
308     return AFPERR_PARAM;
309
310   len = UAM_PASSWD_FILENAME;
311   if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
312                              (void *) &passwdfile, &len) < 0)
313     return AFPERR_PARAM;
314
315   len = (unsigned char) *ibuf++;
316   if ( len > ulen ) {
317         return AFPERR_PARAM;
318   }
319   memcpy(username, ibuf, len );
320   ibuf += len;
321   username[ len ] = '\0';
322   if ((unsigned long) ibuf & 1) /* padding */
323     ++ibuf;
324   
325   if (( randpwd = uam_getname(username, ulen)) == NULL )
326     return AFPERR_PARAM; /* unknown user */
327   
328   LOG(log_info, logtype_uams, "randnum/rand2num login: %s", username);
329   if (uam_checkuser(randpwd) < 0)
330     return AFPERR_NOTAUTH;
331
332   if ((err = randpass(randpwd, passwdfile, seskey,
333                       sizeof(seskey), 0)) != AFP_OK)
334     return err;
335
336   /* get a random number */
337   len = sizeof(randbuf);
338   if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
339                            (void *) randbuf, &len) < 0)
340     return AFPERR_PARAM;
341
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);
347   
348   /* send the random number off */
349   memcpy(rbuf, randbuf, sizeof(randbuf));
350   *rbuflen += sizeof(randbuf);
351   return AFPERR_AUTHCONT;
352 }
353
354
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)
360 {
361   int err = AFP_OK;
362   u_int16_t sessid;
363
364   *rbuflen = 0;
365
366   memcpy(&sessid, ibuf, sizeof(sessid));
367   if (sessid != randhash(obj))
368     return AFPERR_PARAM;
369
370   ibuf += sizeof(sessid);
371
372   err = atalk_encrypt(seskey, randbuf, randbuf);
373   memset(seskey, 0, sizeof(seskey));
374   if (err)
375     return err;
376
377   /* test against what the client sent */
378   if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
379     memset(randbuf, 0, sizeof(randbuf));
380     return AFPERR_NOTAUTH;
381   }
382
383   memset(randbuf, 0, sizeof(randbuf));
384   *uam_pwd = randpwd;
385   return err;
386 }
387
388
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.
393  */
394 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
395                               char *ibuf, int ibuflen, 
396                               char *rbuf, int *rbuflen)
397 {
398   int err = AFP_OK;
399   CryptHandle crypt_handle;
400   u_int16_t sessid;
401   int i;
402
403   *rbuflen = 0;
404
405   /* compare session id */
406   memcpy(&sessid, ibuf, sizeof(sessid));
407   if (sessid != randhash(obj))
408     return AFPERR_PARAM;
409
410   ibuf += sizeof(sessid);
411
412   /* shift key elements left one bit */
413   for (i = 0; i < sizeof(seskey); i++)
414     seskey[i] <<= 1;
415
416   /* encrypt randbuf */
417   err = atalk_encrypt_start(&crypt_handle, seskey);
418   atalk_encrypt_do(crypt_handle, randbuf, randbuf);
419
420   /* test against client's reply */
421   if (memcmp(randbuf, ibuf, sizeof(randbuf))) { /* != */
422     memset(randbuf, 0, sizeof(randbuf));
423     return AFPERR_NOTAUTH;
424   }
425   ibuf += sizeof(randbuf);
426   memset(randbuf, 0, sizeof(randbuf));
427
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));
432
433   *rbuflen = sizeof(randbuf);
434   
435   *uam_pwd = randpwd;
436   return AFP_OK;
437 }
438
439 /* change password  --
440  * NOTE: an FPLogin must already have completed successfully for this
441  *       to work. 
442  */
443 static int randnum_changepw(void *obj, const char *username, 
444                             struct passwd *pwd, char *ibuf,
445                             int ibuflen, char *rbuf, int *rbuflen)
446 {
447     char *passwdfile;
448     int err, len;
449
450     if (uam_checkuser(pwd) < 0)
451       return AFPERR_ACCESS;
452
453     len = UAM_PASSWD_FILENAME;
454     if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
455                              (void *) &passwdfile, &len) < 0)
456       return AFPERR_PARAM;
457
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)
462       return err;
463
464     /* use old passwd to decrypt new passwd */
465     ibuf += PASSWDLEN; /* new passwd */
466     ibuf[PASSWDLEN] = '\0';
467     err = atalk_decrypt(seskey, ibuf, ibuf);
468     if (err)
469       return err;
470
471     /* now use new passwd to decrypt old passwd */
472     ibuf -= PASSWDLEN; /* old passwd */
473     err = atalk_decrypt(ibuf, ibuf, ibuf);
474     if (err)
475       return err;
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;
480 #ifdef USE_CRACKLIB
481     else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
482         err = AFPERR_PWDPOLCY;
483 #endif /* USE_CRACKLIB */
484
485     if (!err) 
486       err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
487
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 */
492
493     if (err)
494       return err;
495
496   return( AFP_OK );
497 }
498
499 static int uam_setup(const char *path)
500 {
501   if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange", 
502                    randnum_login, randnum_logincont, NULL) < 0)
503     return -1;
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");
507     return -1;
508   }
509     
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");
514     return -1;
515   }
516   /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
517     pam_printer);*/
518
519   return 0;
520 }
521
522 static void uam_cleanup(void)
523 {
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");*/
528 }
529
530 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
531   UAM_MODULE_SERVER,
532   UAM_MODULE_VERSION,
533   uam_setup, uam_cleanup
534 };