2 * $Id: afppasswd.c,v 1.13 2003-06-06 22:40:12 srittau Exp $
4 * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
5 * All Rights Reserved. See COPYRIGHT.
7 * format of the password file:
8 * name:****************:****************:********
9 * password last login date failed usage count
11 * ***'s are illegal. they're just place holders for hex values. hex
12 * values that represent actual numbers are in network byte order.
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.
17 * the last two fields aren't currently used by the randnum uams.
19 * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
20 * user syntax: afppasswd
25 #endif /* HAVE_CONFIG_H */
33 #endif /* HAVE_UNISTD_H */
35 #include <sys/types.h>
37 #include <sys/param.h>
40 #endif /* HAVE_FCNTL_H */
43 #include <netatalk/endian.h>
49 #endif /* USE_CRACKLIB */
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)
57 #define PASSWD_ILLEGAL '*'
59 #define FORMAT ":****************:****************:********\n"
61 #define OPTIONS "cafnu:p:"
64 #define HEXPASSWDLEN 16
67 static char buf[MAXPATHLEN + 1];
69 /* FIXME: preparation for i18n */
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)
79 u_int8_t key[HEXPASSWDLEN];
80 DES_key_schedule schedule;
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]);
88 memset(buf + j, 0, sizeof(key) - j);
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]);
98 memset(key + j, 0, sizeof(key) - j);
99 DES_key_sched((DES_cblock *) key, &schedule);
100 memset(key, 0, sizeof(key));
102 DES_ecb_encrypt((DES_cblock *) newpwd, (DES_cblock *) newpwd,
103 &schedule, DES_ENCRYPT);
105 /* decrypt the password */
106 ecb_encrypt((C_Block *) buf, (C_Block *) buf, schedule, DES_DECRYPT);
108 memset(&schedule, 0, sizeof(schedule));
112 const unsigned char hextable[] = "0123456789ABCDEF";
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];
122 /* this matches the code in uam_randnum.c */
123 static int update_passwd(const char *path, const char *name, int flags)
125 char password[PASSWDLEN + 1], *p, *passwd;
129 int keyfd = -1, err = 0;
131 if ((fp = fopen(path, "r+")) == NULL) {
132 fprintf(stderr, _("Can't open password file %s: %s.\n"), path, strerror(errno));
136 /* open the key file if it exists */
138 if (strlen(path) < sizeof(buf) - 5) {
140 keyfd = open(buf, O_RDONLY);
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) {
150 if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
151 fprintf(stderr, _("Your password is disabled. Please see your administrator.\n"));
159 memset(buf, 0, sizeof(buf));
163 if ((flags & OPT_ISROOT) && (flags & OPT_ADDUSER)) {
166 p = strchr(buf, ':') + 1;
167 fwrite(buf, strlen(buf), 1, fp);
169 fprintf(stderr, _("Can't find user %s in %s\n"), name, path);
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"));
186 memset(passwd, 0, strlen(passwd));
189 passwd = getpass(_("Enter NEW AFP password: "));
190 memcpy(password, passwd, sizeof(password));
191 memset(passwd, 0, strlen(passwd));
192 password[PASSWDLEN] = '\0';
194 if (!(flags & OPT_NOCRACK)) {
196 if (str = FascistCheck(password, _PATH_CRACKLIB)) {
197 memset(password, 0, PASSWDLEN);
198 fprintf(stderr, _("Error: %s\n"), str);
203 #endif /* USE_CRACKLIB */
205 passwd = getpass(_("Enter NEW AFP password again: "));
206 if (strcmp(passwd, password) == 0) {
210 memset(passwd, 0, strlen(passwd));
212 convert_passwd(p, password, keyfd);
213 lock.l_type = F_WRLCK;
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);
226 memset(passwd, 0, strlen(passwd));
227 memset(password, 0, PASSWDLEN);
228 fprintf(stderr, _("Passwords don't match!\n"));
240 /* creates a file with all the password entries */
241 static int create_file(const char *path, uid_t minuid)
244 int fd, len, err = 0;
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));
253 while ((pwd = getpwent())) {
254 if (pwd->pw_uid < minuid)
256 /* a little paranoia */
257 if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
259 strcpy(buf, pwd->pw_name);
262 if (write(fd, buf, len) != len) {
263 fprintf(stderr, _("Problem writing to password file %s: %s\n"),
264 path, strerror(errno));
276 static void usage(void)
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"));
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"));
294 int main(int argc, char **argv)
298 uid_t uid_min = UID_START, uid;
299 char *path = _PATH_AFPDPWFILE;
305 flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
307 if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
312 while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
314 case 'c': /* create and initialize password file or specific user */
317 case 'a': /* add a new user */
318 flags |= OPT_ADDUSER;
320 case 'f': /* force an action */
323 case 'u': /* minimum uid to use. default is 100 */
324 uid_min = atoi(optarg);
327 case 'n': /* disable CRACKLIB check */
328 flags |= OPT_NOCRACK;
330 #endif /* USE_CRACKLIB */
331 case 'p': /* path to afppasswd file */
340 if (err || (optind + ((flags & OPT_CREATE) ? 0 :
341 (flags & OPT_ISROOT)) != argc)) {
346 if (flags & OPT_CREATE) {
347 if ((flags & OPT_ISROOT) == 0) {
348 fprintf(stderr, _("Only root can create the password file.\n"));
352 if (!stat(path, &st) && ((flags & OPT_FORCE) == 0)) {
353 fprintf(stderr, _("Password file already exists.\n"));
356 return create_file(path, uid_min);
359 struct passwd *pwd = NULL;
361 if (stat(path, &st) < 0) {
362 fprintf(stderr, _("Password file %s doesn't exist.\n"), path);
366 /* if we're root, we need to specify the username */
367 pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
369 return update_passwd(path, pwd->pw_name, flags);
371 fprintf(stderr, _("Can't get password entry.\n"));