kauth: Fix overflow when writing ticket file
[openafs.git] / src / kauth / manyklog.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 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <rx/xdr.h>
17 #include <lock.h>
18 #include <ubik.h>
19 #include <afs/com_err.h>
20 #include <afs/cellconfig.h>
21 #include <afs/cmd.h>
22
23 #include "kauth.h"
24 #include "kautils.h"
25 #include "assert.h"
26
27
28 /* This code borrowed heavily from the previous version of log.  Here is the
29    intro comment for that program: */
30
31 /*
32         log -- tell the Andrew Cache Manager your password
33         5 June 1985
34         modified February 1986
35
36         Further modified in August 1987 to understand cell IDs.
37  */
38
39 /* Current Usage:
40      klog [principal [password]] [-t] [-c cellname] [-servers <hostlist>]
41
42      where:
43        principal is of the form 'name' or 'name@cell' which provides the
44           cellname.  See the -c option below.
45        password is the user's password.  This form is NOT recommended for
46           interactive users.
47        -t advises klog to write a Kerberos style ticket file in /tmp.
48        -c identifies cellname as the cell in which authentication is to take
49           place.
50        -servers allows the explicit specification of the hosts providing
51           authentication services for the cell being used for authentication.
52  */
53
54 #define KLOGEXIT(code) assert(!code || code >= KAMINERROR); \
55                        rx_Finalize(); \
56                        (!code ? exit(0) : exit((code)-KAMINERROR+1))
57 static int CommandProc(struct cmd_syndesc *, void *);
58
59 static int zero_argc;
60 static char **zero_argv;
61
62 int
63 osi_audit()
64 {
65     return 0;
66 }
67
68 main(argc, argv)
69      int argc;
70      char *argv[];
71 {
72     struct cmd_syndesc *ts;
73     afs_int32 code;
74 #ifdef  AFS_AIX32_ENV
75     /*
76      * The following signal action for AIX is necessary so that in case of a
77      * crash (i.e. core is generated) we can include the user's data section
78      * in the core dump. Unfortunately, by default, only a partial core is
79      * generated which, in many cases, isn't too useful.
80      */
81     struct sigaction nsa;
82
83     sigemptyset(&nsa.sa_mask);
84     nsa.sa_handler = SIG_DFL;
85     nsa.sa_flags = SA_FULLDUMP;
86     sigaction(SIGSEGV, &nsa, NULL);
87 #endif
88     zero_argc = argc;
89     zero_argv = argv;
90
91     ts = cmd_CreateSyntax(NULL, CommandProc, NULL,
92                           "obtain Kerberos authentication");
93
94 #define aXFLAG 0
95 #define aPRINCIPAL 1
96 #define aPASSWORD 2
97 #define aTMP 3
98 #define aCELL 4
99 #define aSERVERS 5
100 #define aPIPE 6
101 #define aSILENT 7
102 #define aLIFETIME 8
103 #define aSETPAG 9
104 #define aNAMES 10
105
106     cmd_AddParm(ts, "-x", CMD_FLAG, CMD_OPTIONAL, "(obsolete, noop)");
107     cmd_Seek(ts, aPRINCIPAL);
108     cmd_AddParm(ts, "-principal", CMD_SINGLE, CMD_OPTIONAL, "user name");
109     cmd_AddParm(ts, "-password", CMD_SINGLE, CMD_OPTIONAL, "user's password");
110     cmd_AddParm(ts, "-tmp", CMD_FLAG, CMD_OPTIONAL,
111                 "write Kerberos-style ticket file in /tmp");
112     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
113     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL,
114                 "explicit list of servers");
115     cmd_AddParm(ts, "-pipe", CMD_FLAG, CMD_OPTIONAL,
116                 "read password from stdin");
117     cmd_AddParm(ts, "-silent", CMD_FLAG, CMD_OPTIONAL, "silent operation");
118     cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL,
119                 "ticket lifetime in hh[:mm[:ss]]");
120     cmd_AddParm(ts, "-setpag", CMD_FLAG, CMD_OPTIONAL,
121                 "Create a new setpag before authenticating");
122     cmd_AddParm(ts, "-names", CMD_LIST, CMD_OPTIONAL, "multiple user names");
123
124     code = cmd_Dispatch(argc, argv);
125     KLOGEXIT(code);
126 }
127
128 extern struct passwd *getpwuid();
129
130 static char *
131 getpipepass()
132 {
133     static char gpbuf[BUFSIZ];
134     /* read a password from stdin, stop on \n or eof */
135     int i, tc;
136     memset(gpbuf, 0, sizeof(gpbuf));
137     for (i = 0; i < (sizeof(gpbuf) - 1); i++) {
138         tc = fgetc(stdin);
139         if (tc == '\n' || tc == EOF)
140             break;
141         gpbuf[i] = tc;
142     }
143     return gpbuf;
144 }
145
146 static int
147 CommandProc(struct cmd_syndesc *as, void *arock)
148 {
149     char name[MAXKTCNAMELEN];
150     char instance[MAXKTCNAMELEN];
151     char cell[MAXKTCREALMLEN];
152     char realm[MAXKTCREALMLEN];
153     afs_uint32 serverList[MAXSERVERS];
154     char *lcell;                /* local cellname */
155     char lrealm[MAXKTCREALMLEN];        /* uppercase copy of local cellname */
156     int code;
157     int i, dosetpag;
158     Date lifetime;              /* requested ticket lifetime */
159
160     struct passwd pwent;
161     struct passwd *pw = &pwent;
162     struct passwd *lclpw = &pwent;
163     char passwd[BUFSIZ];
164
165     static char rn[] = "klog";  /*Routine name */
166     static int Pipe = 0;        /* reading from a pipe */
167     static int Silent = 0;      /* Don't want error messages */
168
169     int explicit;               /* servers specified explicitly */
170     int local;                  /* explicit cell is same a local one */
171     int foundPassword = 0;      /*Not yet, anyway */
172     int foundExplicitCell = 0;  /*Not yet, anyway */
173     int writeTicketFile = 0;    /* write ticket file to /tmp */
174     afs_int32 password_expires = -1;
175
176     char *reason;               /* string describing errors */
177
178     /* blow away command line arguments */
179     for (i = 1; i < zero_argc; i++)
180         memset(zero_argv[i], 0, strlen(zero_argv[i]));
181     zero_argc = 0;
182
183     /* first determine quiet flag based on -silent switch */
184     Silent = (as->parms[aSILENT].items ? 1 : 0);
185     Pipe = (as->parms[aPIPE].items ? 1 : 0);
186
187     /* Determine if we should also do a setpag based on -setpag switch */
188     dosetpag = (as->parms[aSETPAG].items ? 1 : 0);
189
190     if (as->parms[aCELL].items) {
191         /*
192          * cell name explicitly mentioned; take it in if no other cell name
193          * has already been specified and if the name actually appears.  If
194          * the given cell name differs from our own, we don't do a lookup.
195          */
196         foundExplicitCell = 1;
197         strncpy(realm, as->parms[aCELL].items->data, sizeof(realm));
198         /* XXX the following is just a hack to handle the afscell environment XXX */
199         (void)afsconf_GetCellInfo((struct afsconf_dir *)0, realm, 0,
200                                   (struct afsconf_cell *)0);
201     }
202
203     code = ka_Init(0);
204     p if (code || !(lcell = ka_LocalCell())) {
205       nocell:
206         if (!Silent)
207             afs_com_err(rn, code, "Can't get local cell name!");
208         KLOGEXIT(code);
209     }
210     if (code = ka_CellToRealm(lcell, lrealm, 0))
211         goto nocell;
212
213     strcpy(instance, "");
214
215     /* Parse our arguments. */
216
217     if (as->parms[aTMP].items) {
218         writeTicketFile = 1;
219     }
220
221     if (as->parms[aCELL].items) {
222         /*
223          * cell name explicitly mentioned; take it in if no other cell name
224          * has already been specified and if the name actually appears.  If
225          * the given cell name differs from our own, we don't do a lookup.
226          */
227         foundExplicitCell = 1;
228         strncpy(realm, as->parms[aCELL].items->data, sizeof(realm));
229     }
230
231     if (as->parms[aSERVERS].items) {
232         /* explicit server list */
233         int i;
234         struct cmd_item *ip;
235         char *ap[MAXSERVERS + 2];
236
237         for (ip = as->parms[aSERVERS].items, i = 2; ip; ip = ip->next, i++)
238             ap[i] = ip->data;
239         ap[0] = "";
240         ap[1] = "-servers";
241         code = ubik_ParseClientList(i, ap, serverList);
242         if (code) {
243             if (!Silent) {
244                 afs_com_err(rn, code, "could not parse server list");
245             }
246             return code;
247         }
248         explicit = 1;
249     } else
250         explicit = 0;
251
252     if (as->parms[aPRINCIPAL].items) {
253         ka_ParseLoginName(as->parms[aPRINCIPAL].items->data, name, instance,
254                           cell);
255         if (strlen(instance) > 0)
256             if (!Silent) {
257                 fprintf(stderr,
258                         "Non-null instance (%s) may cause strange behavior.\n",
259                         instance);
260             }
261         if (strlen(cell) > 0) {
262             if (foundExplicitCell) {
263                 if (!Silent) {
264                     fprintf(stderr,
265                             "%s: May not specify an explicit cell twice.\n",
266                             rn);
267                 }
268                 return -1;
269             }
270             foundExplicitCell = 1;
271             strncpy(realm, cell, sizeof(realm));
272         }
273         lclpw->pw_name = name;
274     } else {
275         /* No explicit name provided: use Unix uid. */
276         pw = getpwuid(getuid());
277         if (pw == 0) {
278             if (!Silent) {
279                 fprintf(stderr,
280                         "Can't figure out your name in local cell %s from your user id.\n",
281                         lcell);
282                 fprintf(stderr, "Try providing the user name.\n");
283             }
284             KLOGEXIT(KABADARGUMENT);
285         }
286         lclpw = pw;
287     }
288
289     if (as->parms[aPASSWORD].items) {
290         /*
291          * Current argument is the desired password string.  Remember it in
292          * our local buffer, and zero out the argument string - anyone can
293          * see it there with ps!
294          */
295         foundPassword = 1;
296         strncpy(passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
297         memset(as->parms[aPASSWORD].items->data, 0,
298                strlen(as->parms[aPASSWORD].items->data));
299     }
300
301     if (as->parms[aLIFETIME].items) {
302         char *life = as->parms[aLIFETIME].items->data;
303         char *sp;               /* string ptr to rest of life */
304         lifetime = 3600 * strtol(life, &sp, 0); /* hours */
305         if (sp == life) {
306           bad_lifetime:
307             if (!Silent)
308                 fprintf(stderr, "%s: translating '%s' to lifetime\n", rn,
309                         life);
310             return -1;
311         }
312         if (*sp == ':') {
313             life = sp + 1;      /* skip the colon */
314             lifetime += 60 * strtol(life, &sp, 0);      /* minutes */
315             if (sp == life)
316                 goto bad_lifetime;
317             if (*sp == ':') {
318                 life = sp + 1;
319                 lifetime += strtol(life, &sp, 0);       /* seconds */
320                 if (sp == life)
321                     goto bad_lifetime;
322                 if (*sp)
323                     goto bad_lifetime;
324             } else if (*sp)
325                 goto bad_lifetime;
326         } else if (*sp)
327             goto bad_lifetime;
328         if (lifetime > MAXKTCTICKETLIFETIME) {
329             if (!Silent)
330                 fprintf(stderr,
331                         "%s: a lifetime of %.2f hours is too long, must be less than %d.\n",
332                         rn, (double)lifetime / 3600.0,
333                         MAXKTCTICKETLIFETIME / 3600);
334             KLOGEXIT(KABADARGUMENT);
335         }
336     } else
337         lifetime = 0;
338
339     if (!foundExplicitCell)
340         strcpy(realm, lcell);
341     if (code = ka_CellToRealm(realm, realm, &local)) {
342         if (!Silent)
343             afs_com_err(rn, code, "Can't convert cell to realm");
344         KLOGEXIT(code);
345     }
346
347     /* Get the password if it wasn't provided. */
348     if (!foundPassword) {
349         if (Pipe) {
350             strncpy(passwd, getpipepass(), sizeof(passwd));
351         } else {
352             if (ka_UserReadPassword
353                 ("Password:", passwd, sizeof(passwd), &reason)) {
354                 fprintf(stderr, "Unable to login because %s.\n", reason);
355                 KLOGEXIT(KABADARGUMENT);
356             }
357         }
358     }
359
360     if (explicit)
361         ka_ExplicitCell(realm, serverList);
362
363     for (itp = as->parms[aNAMES].items; itp; itp = itp->next) {
364         *name = *instance = *cell = '\0';
365         ka_ParseLoginName(itp->data, name, instance, cell);
366         if (strlen(cell) > 0) {
367             foundExplicitCell = 1;
368             strncpy(realm, cell, sizeof(realm));
369         }
370
371         code =
372             ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION +
373                                        (dosetpag ? KA_USERAUTH_DOSETPAG2 : 0),
374                                        name, instance, realm, passwd,
375                                        lifetime, &password_expires, 0,
376                                        &reason);
377         if (code) {
378             if (!Silent) {
379                 fprintf(stderr, "Unable to authenticate to AFS because %s.\n",
380                         reason);
381             }
382             KLOGEXIT(code);
383         }
384
385         if (password_expires >= 0) {
386             printf("password expires at %ld\n", password_expires);
387         }
388     }
389     return 0;
390 }