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