]> arthur.barton.de Git - netatalk.git/blob - bin/afppasswd/afppasswd.c
Initial revision
[netatalk.git] / bin / afppasswd / afppasswd.c
1 /* 
2  * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
3  * All Rights Reserved. See COPYRIGHT.
4  *
5  * format of the password file:
6  * name:****************:****************:********
7  *      password         last login date  failed usage count
8  *
9  * ***'s are illegal. they're just place holders for hex values. hex
10  * values that represent actual numbers are in network byte order.
11  *
12  * last login date is currently a 4-byte number representing seconds
13  * since 1970 (UTC). there's enough space to extend it to 8 bytes.
14  *
15  * the last two fields aren't currently used by the randnum uams.
16  *
17  * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
18  * user syntax: afppasswd 
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/param.h>
29 #include <fcntl.h>
30 #include <pwd.h>
31
32 #include <netatalk/endian.h>
33
34 #ifdef UAM_RNDNUM
35 #include <des.h>
36
37 #ifdef USE_CRACKLIB
38 #include <crack.h>
39 #endif
40
41 #define OPT_ISROOT  (1 << 0)
42 #define OPT_CREATE  (1 << 1)
43 #define OPT_FORCE   (1 << 2)
44 #define OPT_ADDUSER (1 << 3)
45
46 #define PASSWD_ILLEGAL '*'
47
48 #define FORMAT  ":****************:****************:********\n"
49 #define FORMAT_LEN 44
50 #define OPTIONS "cafu:p:"
51 #define UID_START 100
52
53 #define HEXPASSWDLEN 16
54 #define PASSWDLEN 8
55
56 static char buf[MAXPATHLEN + 1];
57
58 /* if newpwd is null, convert buf from hex to binary. if newpwd isn't
59  * null, convert newpwd to hex and save it in buf. */
60 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
61 static void convert_passwd(char *buf, char *newpwd, const int keyfd)
62 {
63   u_int8_t key[HEXPASSWDLEN];
64   Key_schedule schedule;
65   int i, j;
66
67   if (!newpwd) {
68     /* convert to binary */
69     for (i = j = 0; i < sizeof(key); i += 2, j++)
70       buf[j] = (unhex(buf[i]) << 4) | unhex(buf[i + 1]);
71     if (j <= DES_KEY_SZ)
72       memset(buf + j, 0, sizeof(key) - j);
73   }
74
75   if (keyfd > -1) {
76     lseek(keyfd, 0, SEEK_SET);
77     read(keyfd, key, sizeof(key));
78     /* convert to binary */
79     for (i = j = 0; i < sizeof(key); i += 2, j++)
80       key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
81     if (j <= DES_KEY_SZ)
82       memset(key + j, 0, sizeof(key) - j);
83     key_sched((C_Block *) key, schedule);
84     memset(key, 0, sizeof(key));   
85     if (newpwd) {
86         ecb_encrypt((C_Block *) newpwd, (C_Block *) newpwd, schedule,
87                     DES_ENCRYPT);
88     } else {
89       /* decrypt the password */
90       ecb_encrypt((C_Block *) buf, (C_Block *) buf, schedule, DES_DECRYPT);
91     }
92     memset(schedule, 0, sizeof(schedule));      
93   }
94
95   if (newpwd) {
96     const unsigned char hextable[] = "0123456789ABCDEF";
97
98     /* convert to hex */
99     for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
100       buf[j] = hextable[(newpwd[i] & 0xF0) >> 4];
101       buf[j + 1] = hextable[newpwd[i] & 0x0F];
102     }
103   }
104 }
105
106 /* this matches the code in uam_randnum.c */
107 static int update_passwd(const char *path, const char *name, int flags)
108 {
109   char password[PASSWDLEN + 1], *p, *passwd;
110   FILE *fp;
111   off_t pos;
112   int keyfd = -1, err = 0;
113
114   if ((fp = fopen(path, "r+")) == NULL) {
115     fprintf(stderr, "afppasswd: can't open %s\n", path);
116     return -1;
117   }
118
119   /* open the key file if it exists */
120   strcpy(buf, path);
121   if (strlen(path) < sizeof(buf) - 5) {
122     strcat(buf, ".key");
123     keyfd = open(buf, O_RDONLY);
124   } 
125
126   pos = ftell(fp);
127   memset(buf, 0, sizeof(buf));
128   while (fgets(buf, sizeof(buf), fp)) {
129     if ((p = strchr(buf, ':'))) {
130       /* check for a match */
131       if (strncmp(buf, name, p - buf) == 0) {
132         p++;
133         if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
134           fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
135           break;
136         }
137         goto found_entry;
138       }
139     }
140     pos = ftell(fp);
141     memset(buf, 0, sizeof(buf));
142   }
143
144   if (flags & OPT_ADDUSER) {
145     strcpy(buf, name);
146     strcat(buf, FORMAT);
147     p = strchr(buf, ':') + 1;
148     fwrite(buf, strlen(buf), 1, fp);
149   } else {
150     fprintf(stderr, "afppasswd: can't find %s in %s\n", name, path);
151     err = -1;
152     goto update_done;
153   }
154
155 found_entry:
156   /* need to verify against old password */
157   if ((flags & OPT_ISROOT) == 0) {
158     passwd = getpass("Enter OLD AFP password: ");
159     convert_passwd(p, NULL, keyfd);
160     if (strncmp(passwd, p, PASSWDLEN)) {
161       fprintf(stderr, "afppasswd: invalid password.\n");
162       err = -1;
163       goto update_done;
164     }
165   }
166
167   /* new password */
168   passwd = getpass("Enter NEW AFP password: ");
169   memcpy(password, passwd, sizeof(password));
170   password[PASSWDLEN] = '\0';
171 #ifdef USE_CRACKLIB
172   if ((passwd = FascistCheck(password, _PATH_CRACKLIB))) {
173       fprintf(stderr, "Error: %s\n", passwd);
174       err = -1;
175       goto update_done;
176   }
177 #endif
178
179   passwd = getpass("Enter NEW AFP password again: ");
180   if (strcmp(passwd, password) == 0) {
181      struct flock lock;
182      int fd = fileno(fp);
183
184      convert_passwd(p, password, keyfd);
185      lock.l_type = F_WRLCK;
186      lock.l_start = pos;
187      lock.l_len = 1;
188      lock.l_whence = SEEK_SET;
189      fseek(fp, pos, SEEK_SET);
190      fcntl(fd, F_SETLKW, &lock);
191      fwrite(buf, p - buf + HEXPASSWDLEN, 1, fp);
192      lock.l_type = F_UNLCK;
193      fcntl(fd, F_SETLK, &lock);
194      printf("afppasswd: updated password.\n");
195
196   } else {
197     fprintf(stderr, "afppasswd: passwords don't match!\n");
198     err = -1;
199   }
200
201 update_done:
202   if (keyfd > -1)
203     close(keyfd);
204   fclose(fp);
205   return err;
206 }
207
208
209 /* creates a file with all the password entries */
210 static int create_file(const char *path, uid_t minuid)
211 {
212   struct passwd *pwd;
213   int fd, len, err = 0;
214
215   
216   if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
217     fprintf(stderr, "afppasswd: can't create %s\n", path);
218     return -1;
219   }
220
221   setpwent();
222   while ((pwd = getpwent())) {
223     if (pwd->pw_uid < minuid)
224       continue;
225     /* a little paranoia */
226     if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
227       continue;
228     strcpy(buf, pwd->pw_name);
229     strcat(buf, FORMAT);
230     len = strlen(buf);
231     if (write(fd, buf, len) != len) {
232       fprintf(stderr, "afppasswd: problem writing to %s: %m\n", path);
233       err = -1;
234       break;
235     }
236   }
237   endpwent();
238   close(fd);
239
240   return err;
241 }
242
243
244 int main(int argc, char **argv)
245 {
246   struct stat st;
247   int flags;
248   uid_t uid_min = UID_START, uid;
249   char *path = _PATH_AFPDPWFILE;
250   int i, err = 0;
251
252   extern char *optarg;
253   extern int optind, opterr;
254
255   flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
256
257   if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
258     fprintf(stderr, "Usage: afppasswd\n");
259     return -1;
260   }
261
262   while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
263     switch (i) {
264     case 'c': /* create and initialize password file or specific user */
265       flags |= OPT_CREATE;
266       break;
267     case 'a': /* add a new user */
268       flags |= OPT_ADDUSER;
269     case 'f': /* force an action */
270       flags |= OPT_FORCE;
271       break;
272     case 'u':  /* minimum uid to use. default is 100 */
273       uid_min = atoi(optarg);
274       break;
275     case 'p': /* path to afppasswd file */
276       path = optarg;
277       break;
278     default:
279       err++;
280       break;
281     }
282   }
283
284   if (err || (optind + ((flags & OPT_CREATE) ? 0 : 
285                         (flags & OPT_ISROOT)) != argc)) {
286     fprintf(stderr, "Usage: afppasswd [-acf] [-u minuid] [-p path] [username]\n");
287     return -1;
288   }
289
290   i = stat(path, &st);
291   if (flags & OPT_CREATE) {
292     if ((flags & OPT_ISROOT) == 0) {
293       fprintf(stderr, "afppasswd: only root can create the password file.\n");
294       return -1;
295     }
296
297     if (!i && ((flags & OPT_FORCE) == 0)) {
298       fprintf(stderr, "afppasswd: password file already exists.\n");
299       return -1;
300     }
301     return create_file(path, uid_min);
302
303   } else {
304     struct passwd *pwd = NULL;
305
306     if (i < 0) {
307       fprintf(stderr, "afppasswd: %s doesn't exist.\n", path);
308       return -1;
309     }
310     
311     /* if we're root, we need to specify the username */
312     pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
313     if (pwd)
314       return update_passwd(path, pwd->pw_name, flags);
315
316     fprintf(stderr, "afppasswd: can't get password entry.\n");
317     return -1;
318   }
319 }
320 #else
321
322 main(int argc, char **argv)
323 {
324   fprintf(stderr, "afppasswd is only useful if you're using centralized passwords\n");
325   fprintf(stderr, "for the Random Number authentication methods.\n");
326   return -1;
327 }
328 #endif
329