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