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