kauth-warning-cleanup-20011005
[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((char *) 0, 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         if (!Pipe) com_err (rn, code , "Can't get local cell name!");
251         exit (1);
252     }
253
254     code = rx_Init(0);
255     if (code) {
256         if (!Pipe) com_err (rn, code , "Failed to initialize Rx");
257         exit (1);
258     }
259
260     strcpy (instance, "");
261
262     /* Parse our arguments. */
263
264     if (as->parms[aCELL].items) {
265         /*
266          * cell name explicitly mentioned; take it in if no other cell name
267          * has already been specified and if the name actually appears.  If
268          * the given cell name differs from our own, we don't do a lookup.
269          */
270         foundExplicitCell = 1;
271         strncpy (realm, as->parms[aCELL].items->data, sizeof(realm));
272     }
273
274     if (as->parms[aSERVERS].items) {
275         /* explicit server list */
276         int i;
277         struct cmd_item *ip;
278         char *ap[MAXSERVERS+2];
279
280         for (ip = as->parms[aSERVERS].items, i=2; ip; ip=ip->next, i++)
281             ap[i] = ip->data;
282         ap[0] = "";
283         ap[1] = "-servers";
284         code = ubik_ParseClientList(i, ap, serverList);
285         if (code) {
286             if (!Pipe) com_err (rn, code, "could not parse server list");
287             return code;
288         }
289         lexplicit = 1;
290     }
291
292     if (as->parms[aPRINCIPAL].items) {
293         ka_ParseLoginName (as->parms[aPRINCIPAL].items->data,
294                            name, instance, cell);
295         if (strlen (instance) > 0)
296             if (!Pipe)
297                 fprintf (stderr, "Non-null instance (%s) may cause strange behavior.\n",
298                          instance);
299         if (strlen(cell) > 0) {
300             if (foundExplicitCell) {
301                 if (!Pipe)
302                     fprintf (stderr, "%s: May not specify an explicit cell twice.\n", rn);
303                 return -1;
304             }
305             foundExplicitCell = 1;
306             strncpy (realm, cell, sizeof(realm));
307         }
308         pw->pw_name = name;
309     } else {
310         /* No explicit name provided: use Unix uid. */
311 #ifdef AFS_NT40_ENV
312       userNameLen = 128;
313       if (GetUserName(userName, &userNameLen) == 0) {
314         if (!Pipe) {
315           fprintf (stderr, "Can't figure out your name in local cell %s from your user id.\n", lcell);
316           fprintf (stderr, "Try providing the user name.\n");
317         }
318         exit (1);
319       }
320       pw->pw_name = userName;
321 #else   
322         pw = getpwuid(getuid());
323         if (pw == 0) {
324             if (!Pipe) {
325                 fprintf (stderr, "Can't figure out your name in local cell %s from your user id.\n", lcell);
326                 fprintf (stderr, "Try providing the user name.\n");
327             }
328             exit (1);
329         }
330 #endif
331     } 
332
333     if (as->parms[aPASSWORD].items) {
334         /*
335          * Current argument is the desired password string.  Remember it in
336          * our local buffer, and zero out the argument string - anyone can
337          * see it there with ps!
338          */
339         foundPassword = 1;
340         strncpy (passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
341         memset(as->parms[aPASSWORD].items->data, 0, strlen(as->parms[aPASSWORD].items->data));
342     }
343
344     if (as->parms[aNEWPASSWORD].items) {
345         /*
346          * Current argument is the new password string.  Remember it in
347          * our local buffer, and zero out the argument string - anyone can
348          * see it there with ps!
349          */
350         foundNewPassword = 1;
351         strncpy (npasswd, as->parms[aNEWPASSWORD].items->data,
352                  sizeof(npasswd));
353         memset(as->parms[aNEWPASSWORD].items->data, 0, strlen(as->parms[aNEWPASSWORD].items->data));
354     }
355
356     if (!foundExplicitCell) strcpy (realm, lcell);
357     if (code = ka_CellToRealm (realm, realm, &local)) {
358         if (!Pipe) com_err (rn, code, "Can't convert cell to realm");
359         exit (1);
360     }
361     lcstring (cell, realm, sizeof(cell));
362
363     ka_PrintUserID ("Changing password for '", pw->pw_name, instance, "'");
364     printf (" in cell '%s'.\n", cell);
365
366     /* Get the password if it wasn't provided. */
367     if (!foundPassword) {
368         if (Pipe) getpipepass (passwd, sizeof(passwd));
369         else {
370             code = read_pass (passwd, sizeof(passwd), "Old password: ", 0);
371             if (code || (strlen (passwd) == 0)) {
372                 if (code) code = KAREADPW;
373                 memset(&mitkey, 0, sizeof(mitkey));
374                 memset(&key, 0, sizeof(key));
375                 memset(passwd, 0, sizeof(passwd));
376                 if (code) com_err (rn, code, "reading password");
377                 exit (1);
378             }
379         }
380     } 
381     ka_StringToKey (passwd, realm, &key);
382     des_string_to_key(passwd, &mitkey);
383     give_to_child(passwd);
384
385     /* Get new password if it wasn't provided. */
386     insist = 0;
387     if (!foundNewPassword) {
388         if (Pipe)
389            getpipepass (npasswd, sizeof(npasswd));
390         else {
391             do {
392                 code = read_pass (npasswd, sizeof(npasswd),
393                                   "New password (RETURN to abort): ", 0);
394                 if (code || (strlen (npasswd) == 0)) {
395                     if (code) 
396                        code = KAREADPW;
397                     goto no_change;
398
399                 }
400             } while (password_bad (npasswd));
401
402             code = read_pass (verify, sizeof(verify),
403                                   "Retype new password: ", 0);
404             if (code) {
405               code = KAREADPW;
406               goto no_change;
407             }
408             if (strcmp (verify, npasswd) != 0) {
409               printf ("Mismatch - ");
410               goto no_change;
411             }
412             memset(verify, 0, sizeof(verify));
413           }
414       }
415     if (code = password_bad (npasswd)) {  /* assmt here! */
416         goto no_change_no_msg;
417     }
418
419 #if TRUNCATEPASSWORD
420     if (strlen(npasswd) > 8) {
421         npasswd[8] = 0;
422         fprintf(stderr,
423                 "%s: password too long, only the first 8 chars will be used.\n",
424                 rn);
425     }
426     else
427         npasswd[8] = 0; /* in case the password was exactly 8 chars long */
428 #endif
429     ka_StringToKey (npasswd, realm, &newkey);
430     des_string_to_key(npasswd, &newmitkey);
431     memset(npasswd, 0, sizeof(npasswd));
432
433     if (lexplicit) ka_ExplicitCell (realm, serverList);
434
435     /* Get an connection to kaserver's admin service in desired cell.  Set the
436      * lifetime above the time uncertainty so that badly skewed clocks are
437      * reported when the ticket is decrypted.  Then give us 10 seconds to
438      * actually get our work done if the clocks are skewed by only 14:59.
439      * NOTE: Kerberos lifetime encoding will round this up to next 5 minute
440      * interval, namely 20 minutes. */
441
442 #define ADMIN_LIFETIME (KTC_TIME_UNCERTAINTY+1)
443
444     code = ka_GetAdminToken (pw->pw_name, instance, realm,
445                              &key, ADMIN_LIFETIME, &token, /*!new*/0);
446     if (code == KABADREQUEST) {
447         code = ka_GetAdminToken (pw->pw_name, instance, realm,
448                                  &mitkey, ADMIN_LIFETIME, &token, /*!new*/0);
449         if ((code == KABADREQUEST) && (strlen (passwd) > 8)) {
450             /* try with only the first 8 characters incase they set their password
451              * with an old style passwd program. */
452             char pass8[9];
453             strncpy (pass8, passwd, 8);
454             pass8[8] = 0;
455             ka_StringToKey (pass8, realm, &key);
456             memset(pass8, 0, sizeof(pass8));
457             memset(passwd, 0, sizeof(passwd));
458             code = ka_GetAdminToken (pw->pw_name, instance, realm,
459                                      &key, ADMIN_LIFETIME, &token, /*!new*/0);
460 #ifdef notdef
461             /* the folks in testing really *hate* this message */
462             if (code == 0) {
463                 fprintf (stderr, "Warning: only the first 8 characters of your old password were significant.\n");
464             }
465 #endif
466             if (code == 0) {
467                 if (dess2k == -1)
468                     dess2k=0;
469             }
470         } else {
471             if (dess2k == -1)
472                 dess2k=1;
473         }
474     } else {
475         if (dess2k == -1)
476             dess2k=0;
477     } 
478
479     memset(&mitkey, 0, sizeof(mitkey));
480     memset(&key, 0, sizeof(key));
481     if (code == KAUBIKCALL) com_err (rn, code, "(Authentication Server unavailable, try later)");
482     else if (code) {
483         if (code == KABADREQUEST)
484             fprintf(stderr, "%s: Incorrect old password.\n", rn);
485         else
486             com_err (rn, code, "so couldn't change password");
487     }
488     else {
489         code = ka_AuthServerConn (realm, KA_MAINTENANCE_SERVICE, &token, &conn);
490         if (code) com_err (rn, code, "contacting Admin Server");
491         else {
492             if (dess2k == 1)
493                 code = ka_ChangePassword (pw->pw_name, instance, conn, 0, &newmitkey);
494             else
495                 code = ka_ChangePassword (pw->pw_name, instance, conn, 0, &newkey);
496             memset(&newkey, 0, sizeof(newkey));
497             memset(&newmitkey, 0, sizeof(newmitkey));
498             if (code) {
499               char * reason;
500               reason = (char *) error_message(code);
501               fprintf (stderr, "%s: Password was not changed because %s\n", rn, reason);
502             }
503             else printf("Password changed.\n\n");
504         }
505     }
506     memset(&newkey, 0, sizeof(newkey));
507     memset(&newmitkey, 0, sizeof(newmitkey));
508
509     /* Might need to close down the ubik_Client connection */
510     if (conn) {
511         ubik_ClientDestroy(conn);
512         conn = 0;
513     }
514     rx_Finalize();
515     terminate_child();
516     exit (code);
517
518   no_change:                       /* yuck, yuck, yuck */
519     if (code) com_err (rn, code, "getting new password");
520  no_change_no_msg:
521     memset(&key, 0, sizeof(key));
522     memset(npasswd, 0, sizeof(npasswd));
523     printf("Password for '%s' in cell '%s' unchanged.\n\n", pw->pw_name, cell);
524     terminate_child();
525     exit (code ? code : 1);
526 }