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