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