]> arthur.barton.de Git - netatalk.git/blob - etc/uams/uams_randnum.c
Initial patch for authenticated printing. Requires LaserWriter 8.5.1 or
[netatalk.git] / etc / uams / uams_randnum.c
1 /* 
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.
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <ctype.h>
13 #include <pwd.h>
14 #include <sys/stat.h>
15 #include <sys/param.h>
16
17 #include <syslog.h>
18
19 #include <netatalk/endian.h>
20
21 #include <atalk/afp.h>
22 #include <atalk/uam.h>
23
24
25 #ifdef UAM_RNDNUM
26 #include <des.h>
27
28 #ifdef USE_CRACKLIB
29 #include <crack.h>
30 #endif
31
32 #ifndef __inline__
33 #define __inline__
34 #endif
35
36 #define PASSWDLEN 8
37
38 static C_Block          seskey;
39 static Key_schedule     seskeysched;
40 static struct passwd    *randpwd;
41 static u_int8_t         randbuf[8];
42
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)
47
48
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,
53                                    const int set)
54 {
55   struct stat st;
56   int fd, i;
57   
58   if ( (fd = open(path, (set) ? O_WRONLY : O_RDONLY)) < 0 ) {
59     syslog( LOG_ERR, "Failed to open %s", path);
60     return AFPERR_ACCESS;
61   }
62
63   if ( fstat( fd, &st ) < 0 ) 
64     goto home_passwd_fail;
65   
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
70    */
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;
76   }
77
78   /* get the password */
79   if (set) {
80     if (write(fd, passwd, len) < 0) {
81       syslog( LOG_ERR, "Failed to write to %s", path );
82       goto home_passwd_fail;
83     }
84   } else {
85     if (read(fd, passwd, len) < 0) {
86       syslog( LOG_ERR, "Failed to read from %s", path );
87       goto home_passwd_fail;
88     }
89   
90   /* get rid of pesky characters */
91   for (i = 0; i < len; i++)
92     if ((passwd[i] != ' ') && isspace(passwd[i]))
93       passwd[i] = '\0';
94   }
95
96   close(fd);
97   return AFP_OK;
98
99 home_passwd_fail:
100   close(fd);
101   return AFPERR_ACCESS;
102 }
103
104
105
106 /* 
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 
111  * formats: 
112  * password file:
113  * username:password:last login date:failedcount
114  *
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.
117  *
118  * key file: 
119  * key (in hex) */
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, 
125                      const int set)
126 {
127   u_int8_t key[DES_KEY_SZ*2];
128   char buf[MAXPATHLEN + 1], *p;
129   Key_schedule  schedule;
130   FILE *fp;
131   int i, j, keyfd = -1, err = 0;
132   off_t pos;
133   
134   if ((fp = fopen(path, (set) ? "r+" : "r")) < 0) {
135     syslog( LOG_ERR, "Failed to open %s", path);
136     return AFPERR_ACCESS;
137   }
138   
139   /* open the key file if it exists */
140   strcpy(buf, path);
141   if (pathlen < sizeof(buf) - 5) {
142     strcat(buf, ".key");
143     keyfd = open(buf, O_RDONLY);
144   } 
145   
146   pos = ftell(fp);
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) {
151         p++;
152         if (*p == PASSWD_ILLEGAL) {
153           syslog(LOG_INFO, "invalid password entry for %s", pwd->pw_name);
154           err = AFPERR_ACCESS;
155           goto afppasswd_done;
156         }
157         goto afppasswd_found;
158       }
159     }
160     pos = ftell(fp);
161     memset(buf, 0, sizeof(buf));
162   }
163   err = AFPERR_PARAM;
164   goto afppasswd_done;
165
166 afppasswd_found:
167   if (!set) {
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]);
171     if (j <= DES_KEY_SZ)
172       memset(p + j, 0, sizeof(key) - j);
173   }
174
175   if (keyfd > -1) {
176       /* read in the hex representation of an 8-byte key */
177       read(keyfd, key, sizeof(key));
178
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]);
182       if (j <= DES_KEY_SZ)
183         memset(key + j, 0, sizeof(key) - j);
184       key_sched((C_Block *) key, schedule);
185       memset(key, 0, sizeof(key));
186
187       if (set) {
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,
191                     DES_ENCRYPT);
192       } else {
193         /* decrypt the password */
194         ecb_encrypt((C_Block *) p, (C_Block *) p, schedule, DES_DECRYPT);
195       }
196       memset(schedule, 0, sizeof(schedule));
197   }
198
199   if (set) {
200     const unsigned char hextable[] = "0123456789ABCDEF";
201     struct flock lock;
202     int fd = fileno(fp);
203
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];
208     }
209     memcpy(p, key, sizeof(key));
210
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;
215     lock.l_start = pos;
216     lock.l_len = 1;
217     lock.l_whence = SEEK_SET;
218
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);
224   } else 
225     memcpy(passwd, p, len);
226
227   memset(buf, 0, sizeof(buf));
228
229 afppasswd_done:
230   if (keyfd > -1)
231     close(keyfd);
232   fclose(fp);
233   return err;
234 }
235
236
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) 
242 {
243   int i;
244   uid_t uid = geteuid();
245
246   /* Build pathname to user's '.passwd' file */
247   i = strlen(file);
248   if (*file == '~') {
249     char path[MAXPATHLEN + 1];
250
251     if ( (strlen(pwd->pw_dir) + i - 1) > MAXPATHLEN)
252     return AFPERR_PARAM;
253   
254     strcpy(path,  pwd->pw_dir );
255     strcat(path, "/" );
256     strcat(path, file + 2);
257     if (!uid)
258       seteuid(pwd->pw_uid); /* change ourselves to the user */
259     i = home_passwd(pwd, path, i, passwd, len, set);
260     if (!uid)
261       seteuid(0); /* change ourselves back to root */
262     return i;
263   } 
264
265   if (i > MAXPATHLEN)
266     return AFPERR_PARAM;
267
268   /* handle afppasswd file. we need to make sure that we're root
269    * when we do this. */
270   if (uid)
271     seteuid(0);
272   i = afppasswd(pwd, file, i, passwd, len, set);
273   if (uid)
274     seteuid(uid);
275   return i;
276 }
277
278   
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)
284 {
285   char *username, *passwdfile;
286   u_int16_t sessid;
287   int len, ulen, err;
288   
289   *rbuflen = 0;
290   
291   if (uam_afpserver_option(obj, UAM_OPTION_USERNAME, 
292                            (void *) &username, &ulen) < 0)
293     return AFPERR_PARAM;
294
295   len = UAM_PASSWD_FILENAME;
296   if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
297                              (void *) &passwdfile, &len) < 0)
298     return AFPERR_PARAM;
299
300   len = (unsigned char) *ibuf++;
301   if ( len > ulen ) {
302         return AFPERR_PARAM;
303   }
304   memcpy(username, ibuf, len );
305   ibuf += len;
306   username[ len ] = '\0';
307   if ((unsigned long) ibuf & 1) /* padding */
308     ++ibuf;
309   
310   if (( randpwd = uam_getname(username, ulen)) == NULL )
311     return AFPERR_PARAM; /* unknown user */
312   
313   syslog( LOG_INFO, "randnum/rand2num login: %s", username);
314   if (uam_checkuser(randpwd) < 0)
315     return AFPERR_NOTAUTH;
316
317   if ((err = randpass(randpwd, passwdfile, seskey,
318                       sizeof(seskey), 0)) != AFP_OK)
319     return err;
320
321   /* get a random number */
322   len = sizeof(randbuf);
323   if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM,
324                            (void *) randbuf, &len) < 0)
325     return AFPERR_PARAM;
326
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);
332   
333   /* send the random number off */
334   memcpy(rbuf, randbuf, sizeof(randbuf));
335   *rbuflen += sizeof(randbuf);
336   return AFPERR_AUTHCONT;
337 }
338
339
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)
345 {
346   u_int16_t sessid;
347
348   *rbuflen = 0;
349
350   memcpy(&sessid, ibuf, sizeof(sessid));
351   if (sessid != randhash(obj))
352     return AFPERR_PARAM;
353
354   ibuf += sizeof(sessid);
355
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));
363
364   /* test against what the client sent */
365   if (memcmp( randbuf, ibuf, sizeof(randbuf) )) { /* != */
366     memset(randbuf, 0, sizeof(randbuf));
367     return AFPERR_NOTAUTH;
368   }
369
370   memset(randbuf, 0, sizeof(randbuf));
371   *uam_pwd = randpwd;
372   return AFP_OK;
373 }
374
375
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.
380  */
381 static int rand2num_logincont(void *obj, struct passwd **uam_pwd,
382                               char *ibuf, int ibuflen, 
383                               char *rbuf, int *rbuflen)
384 {
385   u_int16_t sessid;
386   int i;
387
388   *rbuflen = 0;
389
390   /* compare session id */
391   memcpy(&sessid, ibuf, sizeof(sessid));
392   if (sessid != randhash(obj))
393     return AFPERR_PARAM;
394
395   ibuf += sizeof(sessid);
396
397   /* shift key elements left one bit */
398   for (i = 0; i < sizeof(seskey); i++)
399     seskey[i] <<= 1;
400
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);
406
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;
412   }
413   ibuf += sizeof(randbuf);
414   memset(randbuf, 0, sizeof(randbuf));
415
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);
421   
422   *uam_pwd = randpwd;
423   return AFP_OK;
424 }
425
426 /* change password  --
427  * NOTE: an FPLogin must already have completed successfully for this
428  *       to work. 
429  */
430 static int randnum_changepw(void *obj, const char *username, 
431                             struct passwd *pwd, char *ibuf,
432                             int ibuflen, char *rbuf, int *rbuflen)
433 {
434     char *passwdfile;
435     int err, len;
436
437     if (uam_checkuser(pwd) < 0)
438       return AFPERR_ACCESS;
439
440     len = UAM_PASSWD_FILENAME;
441     if (uam_afpserver_option(obj, UAM_OPTION_PASSWDOPT, 
442                              (void *) &passwdfile, &len) < 0)
443       return AFPERR_PARAM;
444
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)
449       return err;
450
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);
456
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;
465 #ifdef USE_CRACKLIB
466     else if (FascistCheck(ibuf + PASSWDLEN, _PATH_CRACKLIB))
467         err = AFPERR_PWDPOLCY;
468 #endif
469
470     if (!err) 
471       err = randpass(pwd, passwdfile, ibuf + PASSWDLEN, sizeof(seskey), 1);
472
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 */
478
479     if (err)
480       return err;
481
482   return( AFP_OK );
483 }
484
485 static int uam_setup(const char *path)
486 {
487   if (uam_register(UAM_SERVER_LOGIN, path, "Randnum exchange", 
488                    randnum_login, randnum_logincont, NULL) < 0)
489     return -1;
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");
493     return -1;
494   }
495     
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");
500     return -1;
501   }
502   /*uam_register(UAM_SERVER_PRINTAUTH, path, "Randnum Exchange",
503     pam_printer);*/
504
505   return 0;
506 }
507
508 static void uam_cleanup(void)
509 {
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");*/
514 }
515
516 UAM_MODULE_EXPORT struct uam_export uams_randnum = {
517   UAM_MODULE_SERVER,
518   UAM_MODULE_VERSION,
519   uam_setup, uam_cleanup
520 };
521
522 #endif /* UAM_RNDNUM */