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