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