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