2456fcb887b093501210956c81fe0416630923ee
[openafs.git] / src / WINNT / afsd / cklog.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 <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <afs/kautils.h>
15 #include <afs/dirpath.h>
16 #include "cm_config.h"
17 #include "cmd.h"
18 #include <winsock2.h>
19
20 #define AFS_KERBEROS_ENV
21
22 #define BAD_ARGUMENT 1
23 #define KLOGEXIT(code) exit(code)
24
25 static int CommandProc(struct cmd_syndesc *, void *);
26
27 static int zero_argc;
28 static char **zero_argv;
29
30 void main (argc, argv)
31   int   argc;
32   char *argv[];
33 {   struct cmd_syndesc *ts;
34     int code;
35     WSADATA WSAjunk;
36
37     zero_argc = argc;
38     zero_argv = argv;
39
40     /* Start up sockets */
41     WSAStartup(0x0101, &WSAjunk);
42
43     ts = cmd_CreateSyntax(NULL, CommandProc, NULL, "obtain Kerberos authentication");
44
45 #define aXFLAG 0
46 #define aPRINCIPAL 1
47 #define aPASSWORD 2
48 #define aCELL 3
49 #define aSERVERS 4
50 #define aPIPE 5
51 #define aSILENT 6
52 #define aLIFETIME 7
53 #define aSETPAG 8
54 #define aTMP 9
55
56
57     cmd_AddParm(ts, "-x", CMD_FLAG, CMD_OPTIONAL, "(obsolete, noop)");
58     cmd_Seek(ts, aPRINCIPAL);
59     cmd_AddParm(ts, "-principal", CMD_SINGLE, CMD_OPTIONAL, "user name");
60     cmd_AddParm(ts, "-password", CMD_SINGLE, CMD_OPTIONAL, "user's password");
61     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
62     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL, "explicit list of servers");
63     cmd_AddParm(ts, "-pipe", CMD_FLAG, CMD_OPTIONAL, "read password from stdin");
64     cmd_AddParm(ts, "-silent", CMD_FLAG, CMD_OPTIONAL, "silent operation");
65     cmd_AddParm(ts, "-lifetime", CMD_SINGLE, CMD_OPTIONAL, "ticket lifetime in hh[:mm[:ss]]");
66     cmd_AddParm(ts, "-setpag", CMD_FLAG, CMD_OPTIONAL, "Create a new setpag before authenticating");
67     cmd_AddParm(ts, "-tmp", CMD_FLAG, CMD_OPTIONAL, "write Kerberos-style ticket file in /tmp");
68
69     code = cmd_Dispatch(argc, argv);
70     KLOGEXIT(code);
71 }
72
73 static char *getpipepass() {
74     static char gpbuf[BUFSIZ];
75     /* read a password from stdin, stop on \n or eof */
76     int i, tc;
77     memset(gpbuf, 0, sizeof(gpbuf));
78     for(i=0; i<(sizeof(gpbuf)-1); i++) {
79         tc = fgetc(stdin);
80         if (tc == '\n' || tc == EOF) break;
81         gpbuf[i] = tc;
82     }
83     return gpbuf;
84 }
85
86 /* good_gets is like gets except that it take a max string length and won't
87  * write past the end of its input buffer.  It returns a variety of negative
88  * numbers in case of errors and zero if there was no characters read (a blank
89  * line for instance).  Otherwise it returns the length of the string read in.
90  */
91
92 static int good_gets (s, max)
93   char *s;
94   int   max;
95 {   int l;                              /* length of string read */
96     if (!fgets (s, max, stdin)) {
97         if (feof(stdin)) return EOF;    /* EOF on input, nothing read */
98         else return -2;                 /* I don't think this can happen */
99     }
100     l = (int)strlen (s);
101     if (l && (s[l-1] == '\n')) s[--l] = 0;
102     return l;
103 }
104
105 static int read_pw_string(char *s, int max)
106 {
107     int ok = 0;
108     HANDLE h;
109     int md;
110
111     /* set no echo */
112     h = GetStdHandle (STD_INPUT_HANDLE);
113     GetConsoleMode (h, &md);
114     SetConsoleMode (h, md & ~ENABLE_ECHO_INPUT);
115
116     while (!ok) {
117         printf("Password:");
118         fflush(stdout);
119         if (good_gets(s, max) <= 0) {
120             printf("\n"); fflush(stdout);
121             if (feof (stdin)) break;    /* just give up */
122             else continue;              /* try again: blank line */
123         }
124         ok = 1;
125     }
126
127     if (!ok)
128         memset(s, 0, max);
129
130     /* reset echo */
131     SetConsoleMode (h, md);
132     printf("\n"); fflush(stdout);
133
134     s[max-1] = 0;                       /* force termination */
135     return !ok;
136 }
137
138 static int
139 CommandProc (struct cmd_syndesc *as, void *arock)
140 {
141     char  name[MAXKTCNAMELEN];
142     char  instance[MAXKTCNAMELEN];
143     char  cell[MAXKTCREALMLEN];
144     char  defaultCell[256];
145     char  realm[MAXKTCREALMLEN];
146     int   code;
147     int   i, dosetpag;
148     int   lifetime;                     /* requested ticket lifetime */
149
150     char passwd[BUFSIZ];
151
152     static char rn[] = "klog";          /*Routine name*/
153     static int Pipe = 0;                /* reading from a pipe */
154     static int Silent = 0;              /* Don't want error messages */
155
156     int foundPassword = 0;              /*Not yet, anyway*/
157     int foundExplicitCell = 0;          /*Not yet, anyway*/
158     int writeTicketFile = 0;          /* write ticket file to /tmp */
159     int password_expires = -1;
160
161     char *reason;                       /* string describing errors */
162
163     name[0] = '\0';
164     instance[0] = '\0';
165     cell[0] = '\0';
166
167
168     /* blow away command line arguments */
169     for (i=1; i<zero_argc; i++) memset (zero_argv[i], 0, strlen(zero_argv[i]));
170     zero_argc = 0;
171
172     /* first determine quiet flag based on -silent switch */
173     Silent = (as->parms[aSILENT].items ? 1 : 0);
174     Pipe = (as->parms[aPIPE].items ? 1 : 0);
175
176     /* Determine if we should also do a setpag based on -setpag switch */
177     dosetpag = (as->parms[aSETPAG].items ? 1 : 0);
178
179     if (as->parms[aTMP].items) {
180        writeTicketFile = 1;
181     }
182
183     cm_GetRootCellName(defaultCell);
184     ka_Init(0);
185
186     /* Parse our arguments. */
187
188     if (as->parms[aCELL].items) {
189         /*
190          * cell name explicitly mentioned; take it in if no other cell name
191          * has already been specified and if the name actually appears.  If
192          * the given cell name differs from our own, we don't do a lookup.
193          */
194         foundExplicitCell = 1;
195         strncpy (realm, as->parms[aCELL].items->data, sizeof(realm));
196     }
197
198     if (as->parms[aSERVERS].items) {
199         fprintf (stderr, "SERVERS option not available.\n");
200     }
201
202     if (as->parms[aPRINCIPAL].items) {
203         ka_ParseLoginName (as->parms[aPRINCIPAL].items->data,
204                            name, instance, cell);
205         if (strlen (instance) > 0)
206             if (!Silent) {
207                 fprintf (stderr,
208                          "Non-null instance (%s) may cause strange behavior.\n",
209                          instance);
210             }
211         if (strlen(cell) > 0) {
212             if (foundExplicitCell) {
213                 if (!Silent) {
214                     fprintf (stderr,
215                           "%s: May not specify an explicit cell twice.\n", rn);
216                 }
217                 return -1;
218             }
219             foundExplicitCell = 1;
220             strncpy (realm, cell, sizeof(realm));
221         }
222     } else {
223         /* No explicit name provided. */
224         DWORD size = GetEnvironmentVariable("USERNAME", name, sizeof(name) - 1);
225         if (size <= 0 || size >= sizeof(name)) {
226             size = sizeof(name) - 1;
227             if (!GetUserName(name, &size)) {
228                 KLOGEXIT( BAD_ARGUMENT );
229             }
230         }
231     }
232
233     if (as->parms[aPASSWORD].items) {
234         /*
235          * Current argument is the desired password string.  Remember it in
236          * our local buffer, and zero out the argument string - anyone can
237          * see it there with ps!
238          */
239         foundPassword = 1;
240         strncpy (passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
241         memset (as->parms[aPASSWORD].items->data, 0,
242                strlen(as->parms[aPASSWORD].items->data));
243     }
244
245     if (as->parms[aLIFETIME].items) {
246         char *life = as->parms[aLIFETIME].items->data;
247         char *sp;                       /* string ptr to rest of life */
248         lifetime = 3600*strtol (life, &sp, 0); /* hours */
249         if (sp == life) {
250 bad_lifetime:
251             if (!Silent) fprintf (stderr, "%s: translating '%s' to lifetime failed\n",
252                                rn, life);
253             return BAD_ARGUMENT;
254         }
255         if (*sp == ':') {
256             life = sp+1;                /* skip the colon */
257             lifetime += 60*strtol (life, &sp, 0); /* minutes */
258             if (sp == life) goto bad_lifetime;
259             if (*sp == ':') {
260                 life = sp+1;
261                 lifetime += strtol (life, &sp, 0); /* seconds */
262                 if (sp == life) goto bad_lifetime;
263                 if (*sp) goto bad_lifetime;
264             } else if (*sp) goto bad_lifetime;
265         } else if (*sp) goto bad_lifetime;
266         if (lifetime > MAXKTCTICKETLIFETIME) {
267             if (!Silent)
268                 fprintf (stderr,
269                 "%s: a lifetime of %.2f hours is too long, must be less than %d.\n",
270                 rn, (double)lifetime/3600.0, MAXKTCTICKETLIFETIME/3600);
271             KLOGEXIT( BAD_ARGUMENT );
272         }
273     } else lifetime = 0;
274
275     if (!foundExplicitCell) strcpy (realm, defaultCell);
276
277     /* Get the password if it wasn't provided. */
278     if (!foundPassword) {
279         if (Pipe) {
280             strncpy(passwd, getpipepass(), sizeof(passwd));
281         }
282         else {
283             if (read_pw_string(passwd, sizeof(passwd)))
284                 reason = "can't read password from terminal";
285             else if (strlen(passwd) == 0)
286                 reason = "zero length password is illegal";
287             else
288                 reason = NULL;
289             if (reason) {
290                 fprintf (stderr, "Unable to login because %s.\n", reason);
291                 KLOGEXIT( BAD_ARGUMENT );
292             }
293         }
294     }
295
296     code = ka_UserAuthenticateGeneral (KA_USERAUTH_VERSION,
297                                        name, instance, realm, passwd, lifetime,
298                                        &password_expires, 0, &reason);
299     memset (passwd, 0, sizeof(passwd));
300     if (code) {
301         if (!Silent) {
302             fprintf (stderr,
303                      "Unable to authenticate to AFS because %s.\n", reason);
304           }
305         KLOGEXIT( code );
306       }
307
308 #ifndef AFS_KERBEROS_ENV
309     if (writeTicketFile) {
310        code = krb_write_ticket_file (realm);
311        if (!Silent) {
312           if (code) 
313               afs_com_err (rn, code, "writing Kerberos ticket file");
314           else fprintf (stderr, "Wrote ticket file to /tmp\n");
315       }
316    }
317 #endif
318  
319 #ifdef DEBUGEXPIRES
320        if (password_expires >= 0) {
321          printf ("password expires at %ld\n", password_expires);
322        }
323 #endif /* DEBUGEXPIRES */
324
325     return 0;
326 }