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