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