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