kauth-prototypes-20090315
[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(struct cmd_syndesc *, void *);
77
78 static int zero_argc;
79 static char **zero_argv;
80 extern int init_child(char *myname);
81 extern int give_to_child(char *pw);
82 extern int terminate_child(char *pw);
83
84 #ifdef AFS_NT40_ENV
85 struct passwd {
86     char *pw_name;
87 };
88 char userName[128];
89 DWORD userNameLen;
90 #endif
91
92 int
93 main(int argc, char *argv[], 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(char *gpbuf, int len)
144 {
145     /* read a password from stdin, stop on \n or eof */
146     register int i, tc;
147     memset(gpbuf, 0, len);
148     for (i = 0; i < len; i++) {
149         tc = fgetc(stdin);
150         if (tc == '\n' || tc == EOF)
151             break;
152         gpbuf[i] = tc;
153     }
154     return;
155 }
156
157 static afs_int32
158 read_pass(char *passwd, int len, char *prompt, int verify)
159 {
160     afs_int32 code;
161     code = read_pw_string(passwd, len, prompt, verify);
162     if (code == -1) {
163         getpipepass(passwd, len);
164         return 0;
165     }
166     return code;
167 }
168
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
182 static char rn[] = "kpasswd";   /* Routine name */
183 static int Pipe = 0;            /* reading from a pipe */
184
185 #if TIMEOUT
186 int
187 timedout(void)
188 {
189     if (!Pipe)
190         fprintf(stderr, "%s: timed out\n", rn);
191     exit(1);
192 }
193 #endif
194
195 char passwd[BUFSIZ], npasswd[BUFSIZ], verify[BUFSIZ];
196
197 int
198 CommandProc(struct cmd_syndesc *as, void *arock)
199 {
200     char name[MAXKTCNAMELEN] = "";
201     char instance[MAXKTCNAMELEN] = "";
202     char cell[MAXKTCREALMLEN] = "";
203     char realm[MAXKTCREALMLEN] = "";
204     afs_int32 serverList[MAXSERVERS];
205     char *lcell;                /* local cellname */
206     int code;
207     int i;
208
209     struct ubik_client *conn = 0;
210     struct ktc_encryptionKey key;
211     struct ktc_encryptionKey mitkey;
212     struct ktc_encryptionKey newkey;
213     struct ktc_encryptionKey newmitkey;
214
215     struct ktc_token token;
216
217     struct passwd pwent;
218     struct passwd *pw = &pwent;
219
220     int insist;                 /* insist on good password quality */
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, &mitkey);
409     give_to_child(passwd);
410
411     /* Get new password if it wasn't provided. */
412     insist = 0;
413     if (!foundNewPassword) {
414         if (Pipe)
415             getpipepass(npasswd, sizeof(npasswd));
416         else {
417             do {
418                 code =
419                     read_pass(npasswd, sizeof(npasswd),
420                               "New password (RETURN to abort): ", 0);
421                 if (code || (strlen(npasswd) == 0)) {
422                     if (code)
423                         code = KAREADPW;
424                     goto no_change;
425
426                 }
427             } while (password_bad(npasswd));
428
429             code =
430                 read_pass(verify, sizeof(verify), "Retype new password: ", 0);
431             if (code) {
432                 code = KAREADPW;
433                 goto no_change;
434             }
435             if (strcmp(verify, npasswd) != 0) {
436                 printf("Mismatch - ");
437                 goto no_change;
438             }
439             memset(verify, 0, sizeof(verify));
440         }
441     }
442     if ((code = password_bad(npasswd))) {       /* assmt here! */
443         goto no_change_no_msg;
444     }
445 #if TRUNCATEPASSWORD
446     if (strlen(npasswd) > 8) {
447         npasswd[8] = 0;
448         fprintf(stderr,
449                 "%s: password too long, only the first 8 chars will be used.\n",
450                 rn);
451     } else
452         npasswd[8] = 0;         /* in case the password was exactly 8 chars long */
453 #endif
454     ka_StringToKey(npasswd, realm, &newkey);
455     des_string_to_key(npasswd, &newmitkey);
456     memset(npasswd, 0, sizeof(npasswd));
457
458     if (lexplicit)
459         ka_ExplicitCell(realm, serverList);
460
461     /* Get an connection to kaserver's admin service in desired cell.  Set the
462      * lifetime above the time uncertainty so that badly skewed clocks are
463      * reported when the ticket is decrypted.  Then give us 10 seconds to
464      * actually get our work done if the clocks are skewed by only 14:59.
465      * NOTE: Kerberos lifetime encoding will round this up to next 5 minute
466      * interval, namely 20 minutes. */
467
468 #define ADMIN_LIFETIME (KTC_TIME_UNCERTAINTY+1)
469
470     code =
471         ka_GetAdminToken(pw->pw_name, instance, realm, &key, ADMIN_LIFETIME,
472                          &token, /*!new */ 0);
473     if (code == KABADREQUEST) {
474         code =
475             ka_GetAdminToken(pw->pw_name, instance, realm, &mitkey,
476                              ADMIN_LIFETIME, &token, /*!new */ 0);
477         if ((code == KABADREQUEST) && (strlen(passwd) > 8)) {
478             /* try with only the first 8 characters incase they set their password
479              * with an old style passwd program. */
480             char pass8[9];
481             strncpy(pass8, passwd, 8);
482             pass8[8] = 0;
483             ka_StringToKey(pass8, realm, &key);
484             memset(pass8, 0, sizeof(pass8));
485             memset(passwd, 0, sizeof(passwd));
486             code = ka_GetAdminToken(pw->pw_name, instance, realm, &key, ADMIN_LIFETIME, &token, /*!new */
487                                     0);
488 #ifdef notdef
489             /* the folks in testing really *hate* this message */
490             if (code == 0) {
491                 fprintf(stderr,
492                         "Warning: only the first 8 characters of your old password were significant.\n");
493             }
494 #endif
495             if (code == 0) {
496                 if (dess2k == -1)
497                     dess2k = 0;
498             }
499         } else {
500             if (dess2k == -1)
501                 dess2k = 1;
502         }
503     } else {
504         if (dess2k == -1)
505             dess2k = 0;
506     }
507
508     memset(&mitkey, 0, sizeof(mitkey));
509     memset(&key, 0, sizeof(key));
510     if (code == KAUBIKCALL)
511         afs_com_err(rn, code, "(Authentication Server unavailable, try later)");
512     else if (code) {
513         if (code == KABADREQUEST)
514             fprintf(stderr, "%s: Incorrect old password.\n", rn);
515         else
516             afs_com_err(rn, code, "so couldn't change password");
517     } else {
518         code =
519             ka_AuthServerConn(realm, KA_MAINTENANCE_SERVICE, &token, &conn);
520         if (code)
521             afs_com_err(rn, code, "contacting Admin Server");
522         else {
523             if (dess2k == 1)
524                 code =
525                     ka_ChangePassword(pw->pw_name, instance, conn, 0,
526                                       &newmitkey);
527             else
528                 code =
529                     ka_ChangePassword(pw->pw_name, instance, conn, 0,
530                                       &newkey);
531             memset(&newkey, 0, sizeof(newkey));
532             memset(&newmitkey, 0, sizeof(newmitkey));
533             if (code) {
534                 char *reason;
535                 reason = (char *)afs_error_message(code);
536                 fprintf(stderr, "%s: Password was not changed because %s\n",
537                         rn, reason);
538             } else
539                 printf("Password changed.\n\n");
540         }
541     }
542     memset(&newkey, 0, sizeof(newkey));
543     memset(&newmitkey, 0, sizeof(newmitkey));
544
545     /* Might need to close down the ubik_Client connection */
546     if (conn) {
547         ubik_ClientDestroy(conn);
548         conn = 0;
549     }
550     rx_Finalize();
551     terminate_child(NULL);
552     exit(code);
553
554   no_change:                    /* yuck, yuck, yuck */
555     if (code)
556         afs_com_err(rn, code, "getting new password");
557   no_change_no_msg:
558     memset(&key, 0, sizeof(key));
559     memset(npasswd, 0, sizeof(npasswd));
560     printf("Password for '%s' in cell '%s' unchanged.\n\n", pw->pw_name,
561            cell);
562     terminate_child(NULL);
563     exit(code ? code : 1);
564 }