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