]> arthur.barton.de Git - netatalk.git/blob - bin/afppasswd/afppasswd.c
I suck; bug fixes, more code cleanup, better error handling, and more new
[netatalk.git] / bin / afppasswd / afppasswd.c
1 /* 
2  * $Id: afppasswd.c,v 1.17 2003-06-09 02:51:34 srittau Exp $
3  *
4  * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
5  * All Rights Reserved. See COPYRIGHT.
6  *
7  * format of the password file:
8  * name:****************:****************:********
9  *      password         last login date  failed usage count
10  *
11  * ***'s are illegal. they're just place holders for hex values. hex
12  * values that represent actual numbers are in network byte order.
13  *
14  * last login date is currently a 4-byte number representing seconds
15  * since 1970 (UTC). there's enough space to extend it to 8 bytes.
16  *
17  * the last two fields aren't currently used by the randnum uams.
18  *
19  * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
20  * user syntax: afppasswd 
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif /* HAVE_CONFIG_H */
26
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif /* HAVE_UNISTD_H */
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/param.h>
39 #ifdef HAVE_FCNTL_H
40 #include <fcntl.h>
41 #endif /* HAVE_FCNTL_H */
42 #include <pwd.h>
43
44 #include <netatalk/endian.h>
45
46 #include <des.h>
47
48 #ifdef USE_CRACKLIB
49 #include <crack.h>
50 #endif /* USE_CRACKLIB */
51
52 #define OPT_ISROOT  (1 << 0)
53 #define OPT_CREATE  (1 << 1)
54 #define OPT_FORCE   (1 << 2)
55 #define OPT_ADDUSER (1 << 3)
56 #define OPT_NOCRACK (1 << 4)
57
58 #define PASSWD_ILLEGAL '*'
59
60 #define FORMAT  ":****************:****************:********\n"
61 #define FORMAT_LEN 44
62 #define OPTIONS "cafnu:p:"
63 #define UID_START 100
64
65 #define PASSWDLEN DES_KEY_SZ
66 #define HEXPASSWDLEN (PASSWDLEN * 2)
67
68 static char buf[MAXPATHLEN + 1];
69
70 /* FIXME: preparation for i18n */
71 #ifndef _
72 #define _(x) (x)
73 #endif
74
75 /* SRC and DST must not overlap. */
76 static void hexify(u_int8_t *dst, const u_int8_t *src)
77 {
78   int i, j;
79
80   for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
81     static const unsigned char hextable[] = "0123456789ABCDEF";
82
83     dst[j]     = hextable[(src[i] & 0xF0) >> 4];
84     dst[j + 1] = hextable[(src[i] & 0x0F)];
85   }
86 }
87
88 /* SRC and DST can overlap. */
89 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
90 static void unhexify(u_int8_t *dst, const u_int8_t *src)
91 {
92   int i, j;
93
94   /* convert to binary */
95   for (i = j = 0; i < HEXPASSWDLEN; i += 2, j++)
96     dst[j] = (unhex(src[i]) << 4) | unhex(src[i + 1]);
97   if (j <= DES_KEY_SZ)
98     memset(dst + j, 0, HEXPASSWDLEN - j);
99 }
100
101 static u_int8_t *retrieve_key(int keyfd)
102 {
103   static u_int8_t key[HEXPASSWDLEN];
104
105   if (lseek(keyfd, 0, SEEK_SET) == -1)
106     return NULL;
107   if (read(keyfd, key, sizeof(key)) < sizeof(key))
108     return NULL;
109
110   unhexify(key, key);
111
112   return key;
113 }
114
115 static int decrypt_passwd(u_int8_t *dst, const u_int8_t *src, int keyfd)
116 {
117   u_int8_t *key;
118   int err = 0;
119
120   key = retrieve_key(keyfd);
121   if (!key)
122     return 0;
123
124   {
125     DES_key_schedule schedule;
126
127     DES_set_key_unchecked((DES_cblock *) key, &schedule);
128     DES_ecb_encrypt((DES_cblock *) src, (DES_cblock *) dst,
129                     &schedule, DES_DECRYPT);
130
131     memset(&schedule, 0, sizeof(schedule));
132   }
133
134   memset(key, 0, HEXPASSWDLEN);
135
136   return err;
137 }
138
139 static int encrypt_passwd(u_int8_t *dst, const u_int8_t *src, int keyfd)
140 {
141   const u_int8_t *key;
142   int err = 0;
143
144   key = retrieve_key(keyfd);
145   if (!key)
146     return 0;
147
148   {
149     DES_key_schedule schedule;
150
151     DES_set_key_unchecked((DES_cblock *) key, &schedule);
152     DES_ecb_encrypt((DES_cblock *) src, (DES_cblock *) dst,
153                     &schedule, DES_ENCRYPT);
154
155     memset(&schedule, 0, sizeof(schedule));      
156   }
157
158   return err;
159 }
160
161 /* this matches the code in uam_randnum.c */
162 static int update_passwd(const char *path, const char *name, int flags)
163 {
164   char password[PASSWDLEN + 1], *p, *passwd;
165   FILE *fp;
166   off_t pos;
167   int found = 0;
168   int keyfd = -1, err = 0;
169
170   if ((fp = fopen(path, "r+")) == NULL) {
171     fprintf(stderr, _("Can't open password file %s: %s.\n"), path, strerror(errno));
172     return 1;
173   }
174
175   /* open the key file if it exists */
176   strcpy(buf, path);
177   if (strlen(path) < sizeof(buf) - 5) {
178     strcat(buf, ".key");
179     keyfd = open(buf, O_RDONLY);
180     if (keyfd == -1 && errno != ENOENT) {
181       fprintf(stderr, _("Can't open key file %s: %s\n"), buf, strerror(errno));
182       err = 1;
183       goto update_done;
184     }
185   } 
186
187   pos = ftell(fp);
188   memset(buf, 0, sizeof(buf));
189   while (fgets(buf, sizeof(buf), fp)) {
190     if ((p = strchr(buf, ':'))) {
191       /* check for a match */
192       if (strncmp(buf, name, p - buf) == 0) {
193         p++;
194         if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
195           fprintf(stderr, _("Your password is disabled. Please see your administrator.\n"));
196           break;
197         }
198         found = 1;
199         break;
200       }
201     }
202     pos = ftell(fp);
203     memset(buf, 0, sizeof(buf));
204   }
205
206   if (!found) {
207     if ((flags & OPT_ISROOT) && (flags & OPT_ADDUSER)) {
208       strcpy(buf, name);
209       strcat(buf, FORMAT);
210       p = strchr(buf, ':') + 1;
211       fwrite(buf, strlen(buf), 1, fp);
212     } else {
213       fprintf(stderr, _("Can't find user %s in %s\n"), name, path);
214       err = 1;
215       goto update_done;
216     }
217   }
218
219   /* need to verify against old password */
220   if ((flags & OPT_ISROOT) == 0) {
221     passwd = getpass(_("Enter OLD AFP password: "));
222     unhexify(p, p);
223     err = decrypt_passwd(p, p, keyfd);
224     if (err) {
225       memset(passwd, 0, strlen(passwd));
226       goto update_done;
227     }
228     if (strncmp(passwd, p, PASSWDLEN)) {
229       memset(passwd, 0, strlen(passwd));
230       fprintf(stderr, _("Wrong password.\n"));
231       err = 1;
232       goto update_done;
233     }
234     memset(passwd, 0, strlen(passwd));
235   }
236
237   /* new password */
238   passwd = getpass(_("Enter NEW AFP password: "));
239   memcpy(password, passwd, sizeof(password));
240   memset(passwd, 0, strlen(passwd));
241   password[PASSWDLEN] = '\0';
242 #ifdef USE_CRACKLIB
243   if (!(flags & OPT_NOCRACK)) {
244     char *str;
245     if (str = FascistCheck(password, _PATH_CRACKLIB)) { 
246       memset(password, 0, PASSWDLEN);
247       fprintf(stderr, _("Error: %s\n"), str);
248       err = 1;
249       goto update_done;
250     } 
251   }
252 #endif /* USE_CRACKLIB */
253
254   passwd = getpass(_("Enter NEW AFP password again: "));
255   if (strcmp(passwd, password) == 0) {
256     struct flock lock;
257     int fd = fileno(fp);
258
259     memset(passwd, 0, strlen(passwd));
260
261     err = encrypt_passwd(password, password, keyfd);
262     if (err)
263       goto update_done;
264     hexify(p, password);
265
266     lock.l_type = F_WRLCK;
267     lock.l_start = pos;
268     lock.l_len = 1;
269     lock.l_whence = SEEK_SET;
270     fseek(fp, pos, SEEK_SET);
271     fcntl(fd, F_SETLKW, &lock);
272     fwrite(buf, p - buf + HEXPASSWDLEN, 1, fp);
273     lock.l_type = F_UNLCK;
274     fcntl(fd, F_SETLK, &lock);
275     printf(_("Updated password.\n"));
276     memset(password, 0, PASSWDLEN);
277
278   } else {
279     memset(passwd, 0, strlen(passwd));
280     memset(password, 0, PASSWDLEN);
281     fprintf(stderr, _("Passwords don't match!\n"));
282     err = 1;
283   }
284
285 update_done:
286   if (keyfd > -1)
287     close(keyfd);
288   fclose(fp);
289   return err;
290 }
291
292
293 /* creates a file with all the password entries */
294 static int create_file(const char *path, uid_t minuid)
295 {
296   struct passwd *pwd;
297   int fd, len, err = 0;
298
299   
300   if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
301     fprintf(stderr, _("Can't create password file %s: %s\n"), path, strerror(errno));
302     return 1;
303   }
304
305   setpwent();
306   while ((pwd = getpwent())) {
307     if (pwd->pw_uid < minuid)
308       continue;
309     /* a little paranoia */
310     if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
311       continue;
312     strcpy(buf, pwd->pw_name);
313     strcat(buf, FORMAT);
314     len = strlen(buf);
315     if (write(fd, buf, len) != len) {
316       fprintf(stderr, _("Problem writing to password file %s: %s\n"),
317               path, strerror(errno));
318       err = 1;
319       break;
320     }
321   }
322   endpwent();
323   close(fd);
324
325   return err;
326 }
327
328
329 static void usage(void)
330 {
331 #ifdef USE_CRACKLIB
332     fprintf(stderr, _("Usage: afppasswd [-acfn] [-u minuid] [-p path] [username]\n"));
333 #else /* USE_CRACKLIB */
334     fprintf(stderr, _("Usage: afppasswd [-acf] [-u minuid] [-p path] [username]\n"));
335 #endif /* USE_CRACKLIB */
336     fprintf(stderr, _("  -a        add a new user\n"));
337     fprintf(stderr, _("  -c        create and initialize password file or specific user\n"));
338     fprintf(stderr, _("  -f        force an action\n"));
339 #ifdef USE_CRACKLIB
340     fprintf(stderr, _("  -n        disable cracklib checking of passwords\n"));
341 #endif /* USE_CRACKLIB */
342     fprintf(stderr, _("  -u uid    minimum uid to use, defaults to 100\n"));
343     fprintf(stderr, _("  -p path   path to afppasswd file\n"));
344 }
345
346
347 int main(int argc, char **argv)
348 {
349   struct stat st;
350   int flags;
351   uid_t uid_min = UID_START, uid;
352   char *path = _PATH_AFPDPWFILE;
353   int i, err = 0;
354
355   extern char *optarg;
356   extern int optind;
357
358   flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
359
360   if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
361     usage();
362     return 1;
363   }
364
365   while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
366     switch (i) {
367     case 'c': /* create and initialize password file or specific user */
368       flags |= OPT_CREATE;
369       break;
370     case 'a': /* add a new user */
371       flags |= OPT_ADDUSER;
372       break;
373     case 'f': /* force an action */
374       flags |= OPT_FORCE;
375       break;
376     case 'u':  /* minimum uid to use. default is 100 */
377       uid_min = atoi(optarg);
378       break;
379 #ifdef USE_CRACKLIB
380     case 'n': /* disable CRACKLIB check */
381       flags |= OPT_NOCRACK;
382       break;
383 #endif /* USE_CRACKLIB */
384     case 'p': /* path to afppasswd file */
385       path = optarg;
386       break;
387     default:
388       err++;
389       break;
390     }
391   }
392   
393   if (err || (optind + ((flags & OPT_CREATE) ? 0 : 
394                         (flags & OPT_ISROOT)) != argc)) {
395     usage();
396     return 1;
397   }
398
399   if (flags & OPT_CREATE) {
400     if ((flags & OPT_ISROOT) == 0) {
401       fprintf(stderr, _("Only root can create the password file.\n"));
402       return 1;
403     }
404
405     if (!stat(path, &st) && ((flags & OPT_FORCE) == 0)) {
406       fprintf(stderr, _("Password file already exists.\n"));
407       return 1;
408     }
409     return create_file(path, uid_min);
410
411   } else {
412     struct passwd *pwd = NULL;
413
414     if (stat(path, &st) < 0) {
415       fprintf(stderr, _("Password file %s doesn't exist.\n"), path);
416       return 1;
417     }
418     
419     /* if we're root, we need to specify the username */
420     pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
421     if (pwd)
422       return update_passwd(path, pwd->pw_name, flags);
423
424     fprintf(stderr, _("Can't get password entry.\n"));
425     return 1;
426   }
427 }