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