Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / kauth / kpasswd.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 /* These two needed for rxgen output to work */
11 #include <afsconfig.h>
12 #include <afs/param.h>
13 #include <afs/stds.h>
14
15 #include <roken.h>
16 #include <afs/opr.h>
17
18 #include <limits.h>
19
20 #include <hcrypto/des.h>
21 #include <hcrypto/ui.h>
22
23 #include <rx/xdr.h>
24 #include <rx/rxkad_convert.h>
25 #include <lock.h>
26 #include <ubik.h>
27 #include <afs/com_err.h>
28 #include <afs/auth.h>
29 #include <afs/cellconfig.h>
30 #include <afs/cmd.h>
31
32 #include "kauth.h"
33 #include "kautils.h"
34 #include "kkids.h"
35
36
37
38 /* This code borrowed heavily from the log program. Here is the intro comment
39 * for that program: */
40
41 /*
42 log -- tell the Andrew Cache Manager your password
43 5 June 1985
44 modified February 1986
45
46 Further modified in August 1987 to understand cell IDs.
47 */
48
49 /* Current Usage:
50 kpasswd [user [password] [newpassword]] [-c cellname] [-servers <hostlist>]
51
52 where:
53 principal is of the form 'name' or 'name@cell' which provides the
54 cellname. See the -c option below.
55 password is the user's password. This form is NOT recommended for
56 interactive users.
57 newpassword is the new password. This form is NOT recommended for
58 interactive users.
59 -c identifies cellname as the cell in which authentication is to take
60 place.
61 -servers allows the explicit specification of the hosts providing
62 authentication services for the cell being used for authentication.
63 This is a debugging option and will disappear.
64 */
65
66 /* The following code to make use of libcmd.a also stolen from klog.c. */
67
68 int CommandProc(struct cmd_syndesc *, void *);
69
70 static int zero_argc;
71 static char **zero_argv;
72
73 #ifdef AFS_NT40_ENV
74 struct passwd {
75 char *pw_name;
76 };
77 char userName[128];
78 DWORD userNameLen;
79 #endif
80
81 int
82 main(int argc, char *argv[], char **envp)
83 {
84 struct cmd_syndesc *ts;
85 afs_int32 code;
86
87 #ifdef AFS_AIX32_ENV
88 /*
89 * The following signal action for AIX is necessary so that in case of a
90 * crash (i.e. core is generated) we can include the user's data section
91 * in the core dump. Unfortunately, by default, only a partial core is
92 * generated which, in many cases, isn't too useful.
93 */
94 struct sigaction nsa;
95
96 sigemptyset(&nsa.sa_mask);
97 nsa.sa_handler = SIG_DFL;
98 nsa.sa_flags = SA_FULLDUMP;
99 sigaction(SIGSEGV, &nsa, NULL);
100 #endif
101
102 zero_argc = argc;
103 zero_argv = argv;
104
105 init_child(*argv);
106 ts = cmd_CreateSyntax(NULL, CommandProc, 0, 0, "change user's password");
107
108 #define aXFLAG 0
109 #define aPRINCIPAL 1
110 #define aPASSWORD 2
111 #define aNEWPASSWORD 3
112 #define aCELL 4
113 #define aSERVERS 5
114 #define aPIPE 6
115
116 cmd_AddParm(ts, "-x", CMD_FLAG, CMD_OPTIONAL, "(obsolete, noop)");
117 cmd_AddParm(ts, "-principal", CMD_SINGLE, CMD_OPTIONAL, "user name");
118 cmd_AddParm(ts, "-password", CMD_SINGLE, CMD_OPTIONAL, "user's password");
119 cmd_AddParm(ts, "-newpassword", CMD_SINGLE, CMD_OPTIONAL,
120 "user's new password");
121 cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
122 cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL,
123 "explicit list of servers");
124 cmd_AddParm(ts, "-pipe", CMD_FLAG, CMD_OPTIONAL, "silent operation");
125
126 code = cmd_Dispatch(argc, argv);
127 exit(code != 0);
128 }
129
130
131 static void
132 getpipepass(char *gpbuf, int len)
133 {
134 /* read a password from stdin, stop on \n or eof */
135 int i, tc;
136 memset(gpbuf, 0, len);
137 for (i = 0; i < len; i++) {
138 tc = fgetc(stdin);
139 if (tc == '\n' || tc == EOF)
140 break;
141 gpbuf[i] = tc;
142 }
143 return;
144 }
145
146 static afs_int32
147 read_pass(char *passwd, int len, char *prompt, int verify)
148 {
149 afs_int32 code;
150 code = UI_UTIL_read_pw_string(passwd, len, prompt, verify);
151 if (code == -1) {
152 getpipepass(passwd, len);
153 return 0;
154 }
155 return code;
156 }
157
158 #if 0
159 static int
160 password_ok(char *newpw, int *insist)
161 {
162 if (insist == 0) {
163 /* see if it is reasonable, but don't get so obnoxious */
164 /* FIXME: null pointer derefence!!! */
165 (*insist)++; /* so we don't get called again */
166 if (strlen(newpw) < 6)
167 return 0;
168 }
169 return 1; /* lie about it */
170 }
171 #endif
172
173 static char rn[] = "kpasswd"; /* Routine name */
174 static int Pipe = 0; /* reading from a pipe */
175
176 #if TIMEOUT
177 int
178 timedout(void)
179 {
180 if (!Pipe)
181 fprintf(stderr, "%s: timed out\n", rn);
182 exit(1);
183 }
184 #endif
185
186 char passwd[BUFSIZ], npasswd[BUFSIZ], verify[BUFSIZ];
187
188 int
189 CommandProc(struct cmd_syndesc *as, void *arock)
190 {
191 char name[MAXKTCNAMELEN] = "";
192 char instance[MAXKTCNAMELEN] = "";
193 char cell[MAXKTCREALMLEN] = "";
194 char realm[MAXKTCREALMLEN] = "";
195 afs_uint32 serverList[MAXSERVERS];
196 char *lcell; /* local cellname */
197 int code;
198 int i;
199
200 struct ubik_client *conn = 0;
201 struct ktc_encryptionKey key;
202 struct ktc_encryptionKey mitkey;
203 struct ktc_encryptionKey newkey;
204 struct ktc_encryptionKey newmitkey;
205
206 struct ktc_token token;
207
208 struct passwd pwent;
209 struct passwd *pw = &pwent;
210
211 int lexplicit = 0; /* servers specified explicitly */
212 int local; /* explicit cell is same a local cell */
213 int foundPassword = 0; /*Not yet, anyway */
214 int foundNewPassword = 0; /*Not yet, anyway */
215 int foundExplicitCell = 0; /*Not yet, anyway */
216 #ifdef DEFAULT_MITV4_STRINGTOKEY
217 int dess2k = 1;
218 #elif DEFAULT_AFS_STRINGTOKEY
219 int dess2k = 0;
220 #else
221 int dess2k = -1;
222 #endif
223
224 /* blow away command line arguments */
225 for (i = 1; i < zero_argc; i++)
226 memset(zero_argv[i], 0, strlen(zero_argv[i]));
227 zero_argc = 0;
228
229 /* first determine quiet flag based on -pipe switch */
230 Pipe = (as->parms[aPIPE].items ? 1 : 0);
231
232 #if TIMEOUT
233 signal(SIGALRM, timedout);
234 alarm(30);
235 #endif
236
237 code = ka_Init(0);
238 if (code || !(lcell = ka_LocalCell())) {
239 #ifndef AFS_FREELANCE_CLIENT
240 if (!Pipe)
241 afs_com_err(rn, code, "Can't get local cell name!");
242 exit(1);
243 #endif
244 }
245
246 code = rx_Init(0);
247 if (code) {
248 if (!Pipe)
249 afs_com_err(rn, code, "Failed to initialize Rx");
250 exit(1);
251 }
252
253 strcpy(instance, "");
254
255 /* Parse our arguments. */
256
257 if (as->parms[aCELL].items) {
258 /*
259 * cell name explicitly mentioned; take it in if no other cell name
260 * has already been specified and if the name actually appears. If
261 * the given cell name differs from our own, we don't do a lookup.
262 */
263 foundExplicitCell = 1;
264 strncpy(realm, as->parms[aCELL].items->data, sizeof(realm));
265 }
266
267 if (as->parms[aSERVERS].items) {
268 /* explicit server list */
269 int i;
270 struct cmd_item *ip;
271 char *ap[MAXSERVERS + 2];
272
273 for (ip = as->parms[aSERVERS].items, i = 2; ip; ip = ip->next, i++)
274 ap[i] = ip->data;
275 ap[0] = "";
276 ap[1] = "-servers";
277 code = ubik_ParseClientList(i, ap, serverList);
278 if (code) {
279 if (!Pipe)
280 afs_com_err(rn, code, "could not parse server list");
281 return code;
282 }
283 lexplicit = 1;
284 }
285
286 if (as->parms[aPRINCIPAL].items) {
287 ka_ParseLoginName(as->parms[aPRINCIPAL].items->data, name, instance,
288 cell);
289 if (strlen(instance) > 0)
290 if (!Pipe)
291 fprintf(stderr,
292 "Non-null instance (%s) may cause strange behavior.\n",
293 instance);
294 if (strlen(cell) > 0) {
295 if (foundExplicitCell) {
296 if (!Pipe)
297 fprintf(stderr,
298 "%s: May not specify an explicit cell twice.\n",
299 rn);
300 return -1;
301 }
302 foundExplicitCell = 1;
303 strncpy(realm, cell, sizeof(realm));
304 }
305 pw->pw_name = name;
306 } else {
307 /* No explicit name provided: use Unix uid. */
308 #ifdef AFS_NT40_ENV
309 userNameLen = 128;
310 if (GetUserName(userName, &userNameLen) == 0) {
311 if (!Pipe) {
312 fprintf(stderr,
313 "Can't figure out your name in local cell %s from your user id.\n",
314 lcell);
315 fprintf(stderr, "Try providing the user name.\n");
316 }
317 exit(1);
318 }
319 pw->pw_name = userName;
320 #else
321 pw = getpwuid(getuid());
322 if (pw == 0) {
323 if (!Pipe) {
324 fprintf(stderr,
325 "Can't figure out your name in local cell %s from your user id.\n",
326 lcell);
327 fprintf(stderr, "Try providing the user name.\n");
328 }
329 exit(1);
330 }
331 #endif
332 }
333
334 if (as->parms[aPASSWORD].items) {
335 /*
336 * Current argument is the desired password string. Remember it in
337 * our local buffer, and zero out the argument string - anyone can
338 * see it there with ps!
339 */
340 foundPassword = 1;
341 strncpy(passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
342 memset(as->parms[aPASSWORD].items->data, 0,
343 strlen(as->parms[aPASSWORD].items->data));
344 }
345
346 if (as->parms[aNEWPASSWORD].items) {
347 /*
348 * Current argument is the new password string. Remember it in
349 * our local buffer, and zero out the argument string - anyone can
350 * see it there with ps!
351 */
352 foundNewPassword = 1;
353 strncpy(npasswd, as->parms[aNEWPASSWORD].items->data,
354 sizeof(npasswd));
355 memset(as->parms[aNEWPASSWORD].items->data, 0,
356 strlen(as->parms[aNEWPASSWORD].items->data));
357 }
358 #ifdef AFS_FREELANCE_CLIENT
359 if (!foundExplicitCell && !lcell) {
360 if (!Pipe)
361 afs_com_err(rn, code, "no cell name provided");
362 exit(1);
363 }
364 #else
365 if (!foundExplicitCell)
366 strcpy(realm, lcell);
367 #endif /* freelance */
368
369 if ((code = ka_CellToRealm(realm, realm, &local))) {
370 if (!Pipe)
371 afs_com_err(rn, code, "Can't convert cell to realm");
372 exit(1);
373 }
374 lcstring(cell, realm, sizeof(cell));
375
376 ka_PrintUserID("Changing password for '", pw->pw_name, instance, "'");
377 printf(" in cell '%s'.\n", cell);
378
379 /* Get the password if it wasn't provided. */
380 if (!foundPassword) {
381 if (Pipe)
382 getpipepass(passwd, sizeof(passwd));
383 else {
384 code = read_pass(passwd, sizeof(passwd), "Old password: ", 0);
385 if (code || (strlen(passwd) == 0)) {
386 if (code)
387 code = KAREADPW;
388 memset(&mitkey, 0, sizeof(mitkey));
389 memset(&key, 0, sizeof(key));
390 memset(passwd, 0, sizeof(passwd));
391 if (code)
392 afs_com_err(rn, code, "reading password");
393 exit(1);
394 }
395 }
396 }
397 ka_StringToKey(passwd, realm, &key);
398 DES_string_to_key(passwd, ktc_to_cblockptr(&mitkey));
399 give_to_child(passwd);
400
401 /* Get new password if it wasn't provided. */
402 if (!foundNewPassword) {
403 if (Pipe)
404 getpipepass(npasswd, sizeof(npasswd));
405 else {
406 do {
407 code =
408 read_pass(npasswd, sizeof(npasswd),
409 "New password (RETURN to abort): ", 0);
410 if (code || (strlen(npasswd) == 0)) {
411 if (code)
412 code = KAREADPW;
413 goto no_change;
414
415 }
416 } while (password_bad(npasswd));
417
418 code =
419 read_pass(verify, sizeof(verify), "Retype new password: ", 0);
420 if (code) {
421 code = KAREADPW;
422 goto no_change;
423 }
424 if (strcmp(verify, npasswd) != 0) {
425 printf("Mismatch - ");
426 goto no_change;
427 }
428 memset(verify, 0, sizeof(verify));
429 }
430 }
431 if ((code = password_bad(npasswd))) { /* assmt here! */
432 goto no_change_no_msg;
433 }
434 #if TRUNCATEPASSWORD
435 if (strlen(npasswd) > 8) {
436 npasswd[8] = 0;
437 fprintf(stderr,
438 "%s: password too long, only the first 8 chars will be used.\n",
439 rn);
440 } else
441 npasswd[8] = 0; /* in case the password was exactly 8 chars long */
442 #endif
443 ka_StringToKey(npasswd, realm, &newkey);
444 DES_string_to_key(npasswd, ktc_to_cblockptr(&newmitkey));
445 memset(npasswd, 0, sizeof(npasswd));
446
447 if (lexplicit)
448 ka_ExplicitCell(realm, serverList);
449
450 /* Get an connection to kaserver's admin service in desired cell. Set the
451 * lifetime above the time uncertainty so that badly skewed clocks are
452 * reported when the ticket is decrypted. Then give us 10 seconds to
453 * actually get our work done if the clocks are skewed by only 14:59.
454 * NOTE: Kerberos lifetime encoding will round this up to next 5 minute
455 * interval, namely 20 minutes. */
456
457 #define ADMIN_LIFETIME (KTC_TIME_UNCERTAINTY+1)
458
459 code =
460 ka_GetAdminToken(pw->pw_name, instance, realm, &key, ADMIN_LIFETIME,
461 &token, /*!new */ 0);
462 if (code == KABADREQUEST) {
463 code =
464 ka_GetAdminToken(pw->pw_name, instance, realm, &mitkey,
465 ADMIN_LIFETIME, &token, /*!new */ 0);
466 if ((code == KABADREQUEST) && (strlen(passwd) > 8)) {
467 /* try with only the first 8 characters incase they set their password
468 * with an old style passwd program. */
469 char pass8[9];
470 strncpy(pass8, passwd, 8);
471 pass8[8] = 0;
472 ka_StringToKey(pass8, realm, &key);
473 memset(pass8, 0, sizeof(pass8));
474 memset(passwd, 0, sizeof(passwd));
475 code = ka_GetAdminToken(pw->pw_name, instance, realm, &key, ADMIN_LIFETIME, &token, /*!new */
476 0);
477 #ifdef notdef
478 /* the folks in testing really *hate* this message */
479 if (code == 0) {
480 fprintf(stderr,
481 "Warning: only the first 8 characters of your old password were significant.\n");
482 }
483 #endif
484 if (code == 0) {
485 if (dess2k == -1)
486 dess2k = 0;
487 }
488 } else {
489 if (dess2k == -1)
490 dess2k = 1;
491 }
492 } else {
493 if (dess2k == -1)
494 dess2k = 0;
495 }
496
497 memset(&mitkey, 0, sizeof(mitkey));
498 memset(&key, 0, sizeof(key));
499 if (code == KAUBIKCALL)
500 afs_com_err(rn, code, "(Authentication Server unavailable, try later)");
501 else if (code) {
502 if (code == KABADREQUEST)
503 fprintf(stderr, "%s: Incorrect old password.\n", rn);
504 else
505 afs_com_err(rn, code, "so couldn't change password");
506 } else {
507 code =
508 ka_AuthServerConn(realm, KA_MAINTENANCE_SERVICE, &token, &conn);
509 if (code)
510 afs_com_err(rn, code, "contacting Admin Server");
511 else {
512 if (dess2k == 1)
513 code =
514 ka_ChangePassword(pw->pw_name, instance, conn, 0,
515 &newmitkey);
516 else
517 code =
518 ka_ChangePassword(pw->pw_name, instance, conn, 0,
519 &newkey);
520 memset(&newkey, 0, sizeof(newkey));
521 memset(&newmitkey, 0, sizeof(newmitkey));
522 if (code) {
523 char *reason;
524 reason = (char *)afs_error_message(code);
525 fprintf(stderr, "%s: Password was not changed because %s\n",
526 rn, reason);
527 } else
528 printf("Password changed.\n\n");
529 }
530 }
531 memset(&newkey, 0, sizeof(newkey));
532 memset(&newmitkey, 0, sizeof(newmitkey));
533
534 /* Might need to close down the ubik_Client connection */
535 if (conn) {
536 ubik_ClientDestroy(conn);
537 conn = 0;
538 }
539 rx_Finalize();
540 terminate_child();
541 exit(code);
542
543 no_change: /* yuck, yuck, yuck */
544 if (code)
545 afs_com_err(rn, code, "getting new password");
546 no_change_no_msg:
547 memset(&key, 0, sizeof(key));
548 memset(npasswd, 0, sizeof(npasswd));
549 printf("Password for '%s' in cell '%s' unchanged.\n\n", pw->pw_name,
550 cell);
551 terminate_child();
552 exit(code ? code : 1);
553 }