1 /* $NetBSD: skeyinit.c,v 1.28 2008/04/05 04:02:06 lukem Exp $ */
3 /* S/KEY v1.1b (skeyinit.c)
6 * Neil M. Haller <nmh@thumper.bellcore.com>
7 * Philip R. Karn <karn@chicago.qualcomm.com>
8 * John S. Walden <jsw@thumper.bellcore.com>
9 * Scott Chasin <chasin@crimelab.com>
12 * Todd C. Miller <Todd.Miller@courtesan.com>
14 * S/KEY initialization and seed update
17 #include <sys/cdefs.h>
20 __RCSID("$NetBSD: skeyinit.c,v 1.28 2008/04/05 04:02:06 lukem Exp $");
23 #include <sys/param.h>
25 #include <sys/resource.h>
42 #define SKEY_NAMELEN 4
45 int main(int argc
, char **argv
)
49 int n
= 0, defaultsetup
= 1, zerokey
= 0, hexmode
= 0;
50 int argpass
= 0, argkey
= 0;
52 char hostname
[MAXHOSTNAMELEN
+ 1];
53 char seed
[SKEY_MAX_PW_LEN
+2], key
[SKEY_BINKEY_SIZE
], defaultseed
[SKEY_MAX_SEED_LEN
+1];
54 char passwd
[SKEY_MAX_PW_LEN
+2], passwd2
[SKEY_MAX_PW_LEN
+2], tbuf
[27], buf
[80];
55 char lastc
, me
[LOGIN_NAME_MAX
+1], *p
, *pw
, *ht
= NULL
;
62 pw
= NULL
; /* XXX gcc -Wuninitialized [sh3] */
65 * Make sure using stdin/stdout/stderr is safe
66 * after opening any file.
68 i
= open(_PATH_DEVNULL
, O_RDWR
);
69 while (i
>= 0 && i
< 2)
75 errx(1, "must be setuid root.");
77 if (gethostname(hostname
, sizeof(hostname
)) < 0)
78 err(1, "gethostname");
81 * Copy the hostname into the default seed, eliminating any
82 * non alpha-numeric characters.
84 for (i
= 0, l
= 0; l
< sizeof(defaultseed
); i
++) {
85 if (hostname
[i
] == '\0') {
86 defaultseed
[l
] = hostname
[i
];
89 if (isalnum((unsigned char)hostname
[i
]))
90 defaultseed
[l
++] = hostname
[i
];
93 defaultseed
[SKEY_NAMELEN
] = '\0';
95 (void)snprintf(tbuf
, sizeof(tbuf
), "%05ld", (long) (now
% 100000));
96 (void)strlcat(defaultseed
, tbuf
, sizeof(defaultseed
));
98 if ((pp
= getpwuid(getuid())) == NULL
)
99 err(1, "no user with uid %ld", (u_long
)getuid());
100 (void)strlcpy(me
, pp
->pw_name
, sizeof(me
));
102 if ((pp
= getpwnam(me
)) == NULL
)
103 err(1, "Who are you?");
104 salt
= pp
->pw_passwd
;
106 while((c
= getopt(argc
, argv
, "k:n:p:t:sxz")) != -1) {
110 if (strlen(optarg
) > SKEY_MAX_PW_LEN
)
111 errx(1, "key too long");
112 strlcpy(passwd
, optarg
, sizeof(passwd
));
113 strlcpy(passwd2
, optarg
, sizeof(passwd
));
117 if(n
< 1 || n
> SKEY_MAX_SEQ
)
118 errx(1, "count must be between 1 and %d", SKEY_MAX_SEQ
);
121 if (strlen(optarg
) >= _PASSWORD_LEN
)
122 errx(1, "password too long");
123 if ((pw
= malloc(_PASSWORD_LEN
+ 1)) == NULL
)
124 err(1, "no memory for password");
125 strlcpy(pw
, optarg
, _PASSWORD_LEN
+ 1);
128 if(skey_set_algorithm(optarg
) == NULL
)
129 errx(1, "Unknown hash algorithm %s", optarg
);
142 errx(1, "usage: %s skeyinit [-sxz] [-k passphrase] "
143 "[-n count] [-p password] [-t hash] [user]",
149 pp
= getpwnam(argv
[optind
]);
151 errx(1, "User %s unknown", argv
[optind
]);
154 if (strcmp(pp
->pw_name
, me
) != 0) {
156 /* Only root can change other's passwds */
157 errx(1, "Permission denied.");
163 pw
= getpass("Password:");
166 if (strcmp(p
, pp
->pw_passwd
)) {
167 errx(1, "Password incorrect.");
171 rval
= skeylookup(&skey
, pp
->pw_name
);
174 err(1, "cannot open database");
176 /* comment out user if asked to */
178 exit(skeyzero(&skey
, pp
->pw_name
));
180 printf("[Updating %s]\n", pp
->pw_name
);
181 printf("Old key: [%s] %s\n", skey_get_algorithm(), skey
.seed
);
184 * lets be nice if they have a skey.seed that
185 * ends in 0-8 just add one
187 l
= strlen(skey
.seed
);
189 lastc
= skey
.seed
[l
- 1];
190 if (isdigit((unsigned char)lastc
) && lastc
!= '9') {
191 (void)strlcpy(defaultseed
, skey
.seed
,
192 sizeof(defaultseed
));
193 defaultseed
[l
- 1] = lastc
+ 1;
195 if (isdigit((unsigned char)lastc
) && lastc
== '9' &&
197 (void)strlcpy(defaultseed
, skey
.seed
,
198 sizeof(defaultseed
));
199 defaultseed
[l
- 1] = '0';
200 defaultseed
[l
] = '0';
201 defaultseed
[l
+ 1] = '\0';
207 errx(1, "You have no entry to zero.");
208 printf("[Adding %s]\n", pp
->pw_name
);
215 /* Set hash type if asked to */
217 /* Need to zero out old key when changing algorithm */
218 if (strcmp(ht
, skey_get_algorithm()) && skey_set_algorithm(ht
))
223 printf("You need the 6 english words generated from the \"skey\" command.\n");
227 printf("Enter sequence count from 1 to %d: ", SKEY_MAX_SEQ
);
228 fgets(buf
, sizeof(buf
), stdin
);
230 if (n
> 0 && n
< SKEY_MAX_SEQ
)
231 break; /* Valid range */
232 printf("\nError: Count must be between 0 and %d\n", SKEY_MAX_SEQ
);
239 printf("Enter new seed [default %s]: ", defaultseed
);
241 fgets(seed
, sizeof(seed
), stdin
);
243 for (p
= seed
; *p
; p
++) {
244 if (isalpha((unsigned char)*p
)) {
245 *p
= tolower((unsigned char)*p
);
246 } else if (!isdigit((unsigned char)*p
)) {
247 (void)puts("Error: seed may only contain alphanumeric characters");
252 break; /* Valid seed */
254 if (strlen(seed
) > SKEY_MAX_SEED_LEN
) {
255 printf("Notice: Seed truncated to %d characters.\n", SKEY_MAX_SEED_LEN
);
256 seed
[SKEY_MAX_SEED_LEN
] = '\0';
259 (void)strlcpy(seed
, defaultseed
, sizeof(seed
));
265 printf("otp-%s %d %s\ns/key access password: ",
266 skey_get_algorithm(), n
, seed
);
267 fgets(buf
, sizeof(buf
), stdin
);
272 puts("Enter 6 English words from secure S/Key calculation.");
274 } else if (buf
[0] == '\0') {
277 if (etob(key
, buf
) == 1 || atob8(key
, buf
) == 0)
278 break; /* Valid format */
279 (void)puts("Invalid format - try again with 6 English words.");
282 /* Get user's secret password */
283 puts("Reminder - Only use this method if you are directly connected\n"
284 " or have an encrypted channel. If you are using telnet\n"
285 " or rlogin, exit with no password and use skeyinit -s.\n");
292 printf("Enter secret password: ");
293 readpass(passwd
, sizeof(passwd
));
294 if (passwd
[0] == '\0')
298 if (strlen(passwd
) < SKEY_MIN_PW_LEN
) {
299 (void)fprintf(stderr
,
300 "Your password must be at least %d characters long.\n", SKEY_MIN_PW_LEN
);
302 } else if (strcmp(passwd
, pp
->pw_name
) == 0) {
303 (void)fputs("Your password may not be the same as your user name.\n", stderr
);
307 else if (strspn(passwd
, "abcdefghijklmnopqrstuvwxyz") == strlen(passwd
)) {
308 (void)fputs("Your password must contain more than just lower case letters.\n"
309 "Whitespace, numbers, and puctuation are suggested.\n", stderr
);
315 printf("Again secret password: ");
316 readpass(passwd2
, sizeof(passwd
));
317 if (passwd2
[0] == '\0')
321 if (strcmp(passwd
, passwd2
) == 0)
324 puts("Passwords do not match.");
327 /* Crunch seed and password into starting key */
328 (void)strlcpy(seed
, defaultseed
, sizeof(seed
));
329 if (keycrunch(key
, seed
, passwd
) != 0)
330 err(2, "key crunch failed");
336 tm
= localtime(&now
);
337 (void)strftime(tbuf
, sizeof(tbuf
), " %b %d,%Y %T", tm
);
339 if ((skey
.val
= (char *)malloc(16 + 1)) == NULL
)
340 err(1, "Can't allocate memory");
342 /* Zero out old key if necessary (entry would change size) */
344 (void)skeyzero(&skey
, pp
->pw_name
);
345 /* Re-open keys file and seek to the end */
346 if (skeylookup(&skey
, pp
->pw_name
) == -1)
347 err(1, "cannot open database");
350 btoa8(skey
.val
, key
);
353 * Obtain an exclusive lock on the key file so we don't
354 * clobber someone authenticating themselves at the same time.
356 for (i
= 0; i
< 300; i
++) {
357 if ((rval
= flock(fileno(skey
.keyfile
), LOCK_EX
|LOCK_NB
)) == 0
358 || errno
!= EWOULDBLOCK
)
360 usleep(100000); /* Sleep for 0.1 seconds */
362 if (rval
== -1) { /* Can't get exclusive lock */
364 err(1, "cannot open database");
367 /* Don't save algorithm type for md4 (keep record length same) */
368 if (strcmp(skey_get_algorithm(), "md4") == 0)
369 (void)fprintf(skey
.keyfile
, "%s %04d %-16s %s %-21s\n",
370 pp
->pw_name
, n
, seed
, skey
.val
, tbuf
);
372 (void)fprintf(skey
.keyfile
, "%s %s %04d %-16s %s %-21s\n",
373 pp
->pw_name
, skey_get_algorithm(), n
, seed
, skey
.val
, tbuf
);
375 (void)fclose(skey
.keyfile
);
377 (void)printf("\nID %s skey is otp-%s %d %s\n", pp
->pw_name
,
378 skey_get_algorithm(), n
, seed
);
379 (void)printf("Next login password: %s\n\n",
380 hexmode
? put8(buf
, key
) : btoe(buf
, key
));