1 1.29 lukem /* $NetBSD: skeyinit.c,v 1.29 2009/04/14 09:53:08 lukem Exp $ */ 2 1.5 cgd 3 1.1 deraadt /* S/KEY v1.1b (skeyinit.c) 4 1.1 deraadt * 5 1.1 deraadt * Authors: 6 1.1 deraadt * Neil M. Haller <nmh (at) thumper.bellcore.com> 7 1.1 deraadt * Philip R. Karn <karn (at) chicago.qualcomm.com> 8 1.1 deraadt * John S. Walden <jsw (at) thumper.bellcore.com> 9 1.1 deraadt * Scott Chasin <chasin (at) crimelab.com> 10 1.1 deraadt * 11 1.13 mjl * Modifications: 12 1.13 mjl * Todd C. Miller <Todd.Miller (at) courtesan.com> 13 1.13 mjl * 14 1.1 deraadt * S/KEY initialization and seed update 15 1.1 deraadt */ 16 1.22 agc 17 1.22 agc #include <sys/cdefs.h> 18 1.22 agc 19 1.22 agc #ifndef lint 20 1.29 lukem __RCSID("$NetBSD: skeyinit.c,v 1.29 2009/04/14 09:53:08 lukem Exp $"); 21 1.22 agc #endif 22 1.1 deraadt 23 1.3 cgd #include <sys/param.h> 24 1.3 cgd #include <sys/time.h> 25 1.3 cgd #include <sys/resource.h> 26 1.3 cgd 27 1.13 mjl #include <ctype.h> 28 1.13 mjl #include <err.h> 29 1.13 mjl #include <errno.h> 30 1.13 mjl #include <fcntl.h> 31 1.17 itohy #include <paths.h> 32 1.13 mjl #include <pwd.h> 33 1.1 deraadt #include <stdio.h> 34 1.5 cgd #include <stdlib.h> 35 1.1 deraadt #include <string.h> 36 1.13 mjl #include <time.h> 37 1.1 deraadt #include <unistd.h> 38 1.3 cgd 39 1.13 mjl #include <skey.h> 40 1.1 deraadt 41 1.13 mjl #ifndef SKEY_NAMELEN 42 1.13 mjl #define SKEY_NAMELEN 4 43 1.13 mjl #endif 44 1.1 deraadt 45 1.13 mjl int main(int argc, char **argv) 46 1.1 deraadt { 47 1.29 lukem int rval, nn, i; 48 1.29 lukem size_t l; 49 1.13 mjl int n = 0, defaultsetup = 1, zerokey = 0, hexmode = 0; 50 1.25 elad int argpass = 0, argkey = 0; 51 1.1 deraadt time_t now; 52 1.9 mrg char hostname[MAXHOSTNAMELEN + 1]; 53 1.13 mjl char seed[SKEY_MAX_PW_LEN+2], key[SKEY_BINKEY_SIZE], defaultseed[SKEY_MAX_SEED_LEN+1]; 54 1.13 mjl char passwd[SKEY_MAX_PW_LEN+2], passwd2[SKEY_MAX_PW_LEN+2], tbuf[27], buf[80]; 55 1.19 christos char lastc, me[LOGIN_NAME_MAX+1], *p, *pw, *ht = NULL; 56 1.11 mycroft const char *salt; 57 1.11 mycroft struct skey skey; 58 1.11 mycroft struct passwd *pp; 59 1.11 mycroft struct tm *tm; 60 1.13 mjl int c; 61 1.17 itohy 62 1.27 he pw = NULL; /* XXX gcc -Wuninitialized [sh3] */ 63 1.27 he 64 1.17 itohy /* 65 1.17 itohy * Make sure using stdin/stdout/stderr is safe 66 1.17 itohy * after opening any file. 67 1.17 itohy */ 68 1.17 itohy i = open(_PATH_DEVNULL, O_RDWR); 69 1.17 itohy while (i >= 0 && i < 2) 70 1.17 itohy i = dup(i); 71 1.17 itohy if (i > 2) 72 1.17 itohy close(i); 73 1.1 deraadt 74 1.13 mjl if (geteuid() != 0) 75 1.13 mjl errx(1, "must be setuid root."); 76 1.1 deraadt 77 1.2 deraadt if (gethostname(hostname, sizeof(hostname)) < 0) 78 1.2 deraadt err(1, "gethostname"); 79 1.14 thorpej 80 1.14 thorpej /* 81 1.14 thorpej * Copy the hostname into the default seed, eliminating any 82 1.14 thorpej * non alpha-numeric characters. 83 1.14 thorpej */ 84 1.14 thorpej for (i = 0, l = 0; l < sizeof(defaultseed); i++) { 85 1.14 thorpej if (hostname[i] == '\0') { 86 1.14 thorpej defaultseed[l] = hostname[i]; 87 1.14 thorpej break; 88 1.14 thorpej } 89 1.24 dsl if (isalnum((unsigned char)hostname[i])) 90 1.14 thorpej defaultseed[l++] = hostname[i]; 91 1.14 thorpej } 92 1.14 thorpej 93 1.13 mjl defaultseed[SKEY_NAMELEN] = '\0'; 94 1.13 mjl (void)time(&now); 95 1.21 itojun (void)snprintf(tbuf, sizeof(tbuf), "%05ld", (long) (now % 100000)); 96 1.21 itojun (void)strlcat(defaultseed, tbuf, sizeof(defaultseed)); 97 1.1 deraadt 98 1.1 deraadt if ((pp = getpwuid(getuid())) == NULL) 99 1.12 christos err(1, "no user with uid %ld", (u_long)getuid()); 100 1.20 itojun (void)strlcpy(me, pp->pw_name, sizeof(me)); 101 1.1 deraadt 102 1.1 deraadt if ((pp = getpwnam(me)) == NULL) 103 1.1 deraadt err(1, "Who are you?"); 104 1.13 mjl salt = pp->pw_passwd; 105 1.1 deraadt 106 1.25 elad while((c = getopt(argc, argv, "k:n:p:t:sxz")) != -1) { 107 1.13 mjl switch(c) { 108 1.25 elad case 'k': 109 1.25 elad argkey = 1; 110 1.25 elad if (strlen(optarg) > SKEY_MAX_PW_LEN) 111 1.25 elad errx(1, "key too long"); 112 1.25 elad strlcpy(passwd, optarg, sizeof(passwd)); 113 1.25 elad strlcpy(passwd2, optarg, sizeof(passwd)); 114 1.25 elad break; 115 1.13 mjl case 'n': 116 1.13 mjl n = atoi(optarg); 117 1.13 mjl if(n < 1 || n > SKEY_MAX_SEQ) 118 1.13 mjl errx(1, "count must be between 1 and %d", SKEY_MAX_SEQ); 119 1.13 mjl break; 120 1.25 elad case 'p': 121 1.25 elad if (strlen(optarg) >= _PASSWORD_LEN) 122 1.25 elad errx(1, "password too long"); 123 1.25 elad if ((pw = malloc(_PASSWORD_LEN + 1)) == NULL) 124 1.25 elad err(1, "no memory for password"); 125 1.25 elad strlcpy(pw, optarg, _PASSWORD_LEN + 1); 126 1.25 elad break; 127 1.13 mjl case 't': 128 1.13 mjl if(skey_set_algorithm(optarg) == NULL) 129 1.13 mjl errx(1, "Unknown hash algorithm %s", optarg); 130 1.13 mjl ht = optarg; 131 1.13 mjl break; 132 1.13 mjl case 's': 133 1.13 mjl defaultsetup = 0; 134 1.13 mjl break; 135 1.13 mjl case 'x': 136 1.13 mjl hexmode = 1; 137 1.13 mjl break; 138 1.13 mjl case 'z': 139 1.13 mjl zerokey = 1; 140 1.13 mjl break; 141 1.13 mjl default: 142 1.26 wiz errx(1, "usage: %s skeyinit [-sxz] [-k passphrase] " 143 1.26 wiz "[-n count] [-p password] [-t hash] [user]", 144 1.26 wiz argv[0]); 145 1.13 mjl } 146 1.13 mjl } 147 1.13 mjl 148 1.13 mjl if(argc > optind) { 149 1.13 mjl pp = getpwnam(argv[optind]); 150 1.13 mjl if (pp == NULL) 151 1.13 mjl errx(1, "User %s unknown", argv[optind]); 152 1.13 mjl } 153 1.1 deraadt 154 1.1 deraadt if (strcmp(pp->pw_name, me) != 0) { 155 1.1 deraadt if (getuid() != 0) { 156 1.1 deraadt /* Only root can change other's passwds */ 157 1.13 mjl errx(1, "Permission denied."); 158 1.1 deraadt } 159 1.1 deraadt } 160 1.1 deraadt 161 1.1 deraadt if (getuid() != 0) { 162 1.25 elad if (!argpass) 163 1.25 elad pw = getpass("Password:"); 164 1.1 deraadt p = crypt(pw, salt); 165 1.1 deraadt 166 1.13 mjl if (strcmp(p, pp->pw_passwd)) { 167 1.13 mjl errx(1, "Password incorrect."); 168 1.1 deraadt } 169 1.1 deraadt } 170 1.13 mjl 171 1.1 deraadt rval = skeylookup(&skey, pp->pw_name); 172 1.1 deraadt switch (rval) { 173 1.1 deraadt case -1: 174 1.1 deraadt err(1, "cannot open database"); 175 1.1 deraadt case 0: 176 1.13 mjl /* comment out user if asked to */ 177 1.13 mjl if (zerokey) 178 1.13 mjl exit(skeyzero(&skey, pp->pw_name)); 179 1.13 mjl 180 1.1 deraadt printf("[Updating %s]\n", pp->pw_name); 181 1.13 mjl printf("Old key: [%s] %s\n", skey_get_algorithm(), skey.seed); 182 1.1 deraadt 183 1.1 deraadt /* 184 1.1 deraadt * lets be nice if they have a skey.seed that 185 1.1 deraadt * ends in 0-8 just add one 186 1.1 deraadt */ 187 1.1 deraadt l = strlen(skey.seed); 188 1.1 deraadt if (l > 0) { 189 1.1 deraadt lastc = skey.seed[l - 1]; 190 1.12 christos if (isdigit((unsigned char)lastc) && lastc != '9') { 191 1.20 itojun (void)strlcpy(defaultseed, skey.seed, 192 1.20 itojun sizeof(defaultseed)); 193 1.1 deraadt defaultseed[l - 1] = lastc + 1; 194 1.1 deraadt } 195 1.12 christos if (isdigit((unsigned char)lastc) && lastc == '9' && 196 1.12 christos l < 16) { 197 1.21 itojun (void)strlcpy(defaultseed, skey.seed, 198 1.20 itojun sizeof(defaultseed)); 199 1.1 deraadt defaultseed[l - 1] = '0'; 200 1.1 deraadt defaultseed[l] = '0'; 201 1.1 deraadt defaultseed[l + 1] = '\0'; 202 1.1 deraadt } 203 1.1 deraadt } 204 1.1 deraadt break; 205 1.1 deraadt case 1: 206 1.13 mjl if (zerokey) 207 1.13 mjl errx(1, "You have no entry to zero."); 208 1.1 deraadt printf("[Adding %s]\n", pp->pw_name); 209 1.1 deraadt break; 210 1.1 deraadt } 211 1.13 mjl 212 1.28 lukem if (n==0) 213 1.28 lukem n = 100; 214 1.13 mjl 215 1.13 mjl /* Set hash type if asked to */ 216 1.13 mjl if (ht) { 217 1.13 mjl /* Need to zero out old key when changing algorithm */ 218 1.13 mjl if (strcmp(ht, skey_get_algorithm()) && skey_set_algorithm(ht)) 219 1.13 mjl zerokey = 1; 220 1.13 mjl } 221 1.1 deraadt 222 1.1 deraadt if (!defaultsetup) { 223 1.13 mjl printf("You need the 6 english words generated from the \"skey\" command.\n"); 224 1.1 deraadt for (i = 0;; i++) { 225 1.1 deraadt if (i >= 2) 226 1.1 deraadt exit(1); 227 1.13 mjl printf("Enter sequence count from 1 to %d: ", SKEY_MAX_SEQ); 228 1.13 mjl fgets(buf, sizeof(buf), stdin); 229 1.13 mjl n = atoi(buf); 230 1.13 mjl if (n > 0 && n < SKEY_MAX_SEQ) 231 1.1 deraadt break; /* Valid range */ 232 1.13 mjl printf("\nError: Count must be between 0 and %d\n", SKEY_MAX_SEQ); 233 1.13 mjl } 234 1.13 mjl 235 1.13 mjl for (i = 0;; i++) { 236 1.13 mjl if (i >= 2) 237 1.13 mjl exit(1); 238 1.13 mjl 239 1.13 mjl printf("Enter new seed [default %s]: ", defaultseed); 240 1.13 mjl fflush(stdout); 241 1.13 mjl fgets(seed, sizeof(seed), stdin); 242 1.13 mjl rip(seed); 243 1.13 mjl for (p = seed; *p; p++) { 244 1.24 dsl if (isalpha((unsigned char)*p)) { 245 1.24 dsl *p = tolower((unsigned char)*p); 246 1.24 dsl } else if (!isdigit((unsigned char)*p)) { 247 1.13 mjl (void)puts("Error: seed may only contain alphanumeric characters"); 248 1.13 mjl break; 249 1.13 mjl } 250 1.13 mjl } 251 1.13 mjl if (*p == '\0') 252 1.13 mjl break; /* Valid seed */ 253 1.1 deraadt } 254 1.13 mjl if (strlen(seed) > SKEY_MAX_SEED_LEN) { 255 1.13 mjl printf("Notice: Seed truncated to %d characters.\n", SKEY_MAX_SEED_LEN); 256 1.13 mjl seed[SKEY_MAX_SEED_LEN] = '\0'; 257 1.1 deraadt } 258 1.1 deraadt if (seed[0] == '\0') 259 1.20 itojun (void)strlcpy(seed, defaultseed, sizeof(seed)); 260 1.1 deraadt 261 1.1 deraadt for (i = 0;; i++) { 262 1.1 deraadt if (i >= 2) 263 1.1 deraadt exit(1); 264 1.1 deraadt 265 1.13 mjl printf("otp-%s %d %s\ns/key access password: ", 266 1.13 mjl skey_get_algorithm(), n, seed); 267 1.13 mjl fgets(buf, sizeof(buf), stdin); 268 1.13 mjl rip(buf); 269 1.13 mjl backspace(buf); 270 1.1 deraadt 271 1.13 mjl if (buf[0] == '?') { 272 1.13 mjl puts("Enter 6 English words from secure S/Key calculation."); 273 1.1 deraadt continue; 274 1.13 mjl } else if (buf[0] == '\0') { 275 1.1 deraadt exit(1); 276 1.1 deraadt } 277 1.13 mjl if (etob(key, buf) == 1 || atob8(key, buf) == 0) 278 1.1 deraadt break; /* Valid format */ 279 1.13 mjl (void)puts("Invalid format - try again with 6 English words."); 280 1.1 deraadt } 281 1.1 deraadt } else { 282 1.13 mjl /* Get user's secret password */ 283 1.13 mjl puts("Reminder - Only use this method if you are directly connected\n" 284 1.13 mjl " or have an encrypted channel. If you are using telnet\n" 285 1.13 mjl " or rlogin, exit with no password and use skeyinit -s.\n"); 286 1.13 mjl 287 1.13 mjl for (i = 0;; i++) { 288 1.1 deraadt if (i >= 2) 289 1.1 deraadt exit(1); 290 1.1 deraadt 291 1.25 elad if (!argkey) { 292 1.25 elad printf("Enter secret password: "); 293 1.25 elad readpass(passwd, sizeof(passwd)); 294 1.25 elad if (passwd[0] == '\0') 295 1.25 elad exit(1); 296 1.25 elad } 297 1.1 deraadt 298 1.13 mjl if (strlen(passwd) < SKEY_MIN_PW_LEN) { 299 1.13 mjl (void)fprintf(stderr, 300 1.13 mjl "Your password must be at least %d characters long.\n", SKEY_MIN_PW_LEN); 301 1.13 mjl continue; 302 1.13 mjl } else if (strcmp(passwd, pp->pw_name) == 0) { 303 1.13 mjl (void)fputs("Your password may not be the same as your user name.\n", stderr); 304 1.13 mjl continue; 305 1.13 mjl } 306 1.13 mjl #if 0 307 1.13 mjl else if (strspn(passwd, "abcdefghijklmnopqrstuvwxyz") == strlen(passwd)) { 308 1.13 mjl (void)fputs("Your password must contain more than just lower case letters.\n" 309 1.13 mjl "Whitespace, numbers, and puctuation are suggested.\n", stderr); 310 1.13 mjl continue; 311 1.13 mjl } 312 1.13 mjl #endif 313 1.25 elad 314 1.25 elad if (!argkey) { 315 1.25 elad printf("Again secret password: "); 316 1.25 elad readpass(passwd2, sizeof(passwd)); 317 1.25 elad if (passwd2[0] == '\0') 318 1.25 elad exit(1); 319 1.25 elad } 320 1.1 deraadt 321 1.1 deraadt if (strcmp(passwd, passwd2) == 0) 322 1.1 deraadt break; 323 1.1 deraadt 324 1.13 mjl puts("Passwords do not match."); 325 1.1 deraadt } 326 1.1 deraadt 327 1.1 deraadt /* Crunch seed and password into starting key */ 328 1.20 itojun (void)strlcpy(seed, defaultseed, sizeof(seed)); 329 1.1 deraadt if (keycrunch(key, seed, passwd) != 0) 330 1.1 deraadt err(2, "key crunch failed"); 331 1.1 deraadt nn = n; 332 1.1 deraadt while (nn-- != 0) 333 1.1 deraadt f(key); 334 1.1 deraadt } 335 1.13 mjl (void)time(&now); 336 1.1 deraadt tm = localtime(&now); 337 1.13 mjl (void)strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm); 338 1.1 deraadt 339 1.13 mjl if ((skey.val = (char *)malloc(16 + 1)) == NULL) 340 1.13 mjl err(1, "Can't allocate memory"); 341 1.13 mjl 342 1.16 wiz /* Zero out old key if necessary (entry would change size) */ 343 1.13 mjl if (zerokey) { 344 1.13 mjl (void)skeyzero(&skey, pp->pw_name); 345 1.13 mjl /* Re-open keys file and seek to the end */ 346 1.13 mjl if (skeylookup(&skey, pp->pw_name) == -1) 347 1.13 mjl err(1, "cannot open database"); 348 1.13 mjl } 349 1.1 deraadt 350 1.1 deraadt btoa8(skey.val, key); 351 1.1 deraadt 352 1.13 mjl /* 353 1.13 mjl * Obtain an exclusive lock on the key file so we don't 354 1.13 mjl * clobber someone authenticating themselves at the same time. 355 1.13 mjl */ 356 1.13 mjl for (i = 0; i < 300; i++) { 357 1.13 mjl if ((rval = flock(fileno(skey.keyfile), LOCK_EX|LOCK_NB)) == 0 358 1.13 mjl || errno != EWOULDBLOCK) 359 1.13 mjl break; 360 1.13 mjl usleep(100000); /* Sleep for 0.1 seconds */ 361 1.13 mjl } 362 1.13 mjl if (rval == -1) { /* Can't get exclusive lock */ 363 1.13 mjl errno = EAGAIN; 364 1.13 mjl err(1, "cannot open database"); 365 1.13 mjl } 366 1.13 mjl 367 1.13 mjl /* Don't save algorithm type for md4 (keep record length same) */ 368 1.13 mjl if (strcmp(skey_get_algorithm(), "md4") == 0) 369 1.13 mjl (void)fprintf(skey.keyfile, "%s %04d %-16s %s %-21s\n", 370 1.13 mjl pp->pw_name, n, seed, skey.val, tbuf); 371 1.13 mjl else 372 1.13 mjl (void)fprintf(skey.keyfile, "%s %s %04d %-16s %s %-21s\n", 373 1.13 mjl pp->pw_name, skey_get_algorithm(), n, seed, skey.val, tbuf); 374 1.13 mjl 375 1.13 mjl (void)fclose(skey.keyfile); 376 1.13 mjl 377 1.13 mjl (void)printf("\nID %s skey is otp-%s %d %s\n", pp->pw_name, 378 1.13 mjl skey_get_algorithm(), n, seed); 379 1.13 mjl (void)printf("Next login password: %s\n\n", 380 1.13 mjl hexmode ? put8(buf, key) : btoe(buf, key)); 381 1.1 deraadt 382 1.13 mjl return(0); 383 1.1 deraadt } 384