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