Standardize License information
[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 KABADARGUMENT 1
23 #define KLOGEXIT(code) exit(code)
24
25 int CommandProc();
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((char *) 0, CommandProc, 0, "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     register 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 = 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 CommandProc (as, arock)
139   char *arock;
140   struct cmd_syndesc *as;
141 {
142     char  name[MAXKTCNAMELEN];
143     char  instance[MAXKTCNAMELEN];
144     char  cell[MAXKTCREALMLEN];
145     char  defaultCell[256];
146     char  realm[MAXKTCREALMLEN];
147     int   code;
148     int   i, dosetpag;
149     int   lifetime;                     /* requested ticket lifetime */
150
151     char passwd[BUFSIZ];
152
153     static char rn[] = "klog";          /*Routine name*/
154     static int Pipe = 0;                /* reading from a pipe */
155     static int Silent = 0;              /* Don't want error messages */
156
157     int foundPassword = 0;              /*Not yet, anyway*/
158     int foundExplicitCell = 0;          /*Not yet, anyway*/
159     int writeTicketFile = 0;          /* write ticket file to /tmp */
160     int password_expires = -1;
161
162     char *reason;                       /* string describing errors */
163
164     name[0] = '\0';
165     instance[0] = '\0';
166     cell[0] = '\0';
167
168
169     /* blow away command line arguments */
170     for (i=1; i<zero_argc; i++) memset (zero_argv[i], 0, strlen(zero_argv[i]));
171     zero_argc = 0;
172
173     /* first determine quiet flag based on -silent switch */
174     Silent = (as->parms[aSILENT].items ? 1 : 0);
175     Pipe = (as->parms[aPIPE].items ? 1 : 0);
176
177     /* Determine if we should also do a setpag based on -setpag switch */
178     dosetpag = (as->parms[aSETPAG].items ? 1 : 0);
179
180     if (as->parms[aTMP].items) {
181        writeTicketFile = 1;
182     }
183
184     cm_GetRootCellName(defaultCell);
185     ka_Init(0);
186
187     /* Parse our arguments. */
188
189     if (as->parms[aCELL].items) {
190         /*
191          * cell name explicitly mentioned; take it in if no other cell name
192          * has already been specified and if the name actually appears.  If
193          * the given cell name differs from our own, we don't do a lookup.
194          */
195         foundExplicitCell = 1;
196         strncpy (realm, as->parms[aCELL].items->data, sizeof(realm));
197     }
198
199     if (as->parms[aSERVERS].items) {
200         fprintf (stderr, "SERVERS option not available.\n");
201     }
202
203     if (as->parms[aPRINCIPAL].items) {
204         ka_ParseLoginName (as->parms[aPRINCIPAL].items->data,
205                            name, instance, cell);
206         if (strlen (instance) > 0)
207             if (!Silent) {
208                 fprintf (stderr,
209                          "Non-null instance (%s) may cause strange behavior.\n",
210                          instance);
211             }
212         if (strlen(cell) > 0) {
213             if (foundExplicitCell) {
214                 if (!Silent) {
215                     fprintf (stderr,
216                           "%s: May not specify an explicit cell twice.\n", rn);
217                 }
218                 return -1;
219             }
220             foundExplicitCell = 1;
221             strncpy (realm, cell, sizeof(realm));
222         }
223     } else {
224         /* No explicit name provided. */
225         DWORD size = GetEnvironmentVariable("USERNAME", name, sizeof(name) - 1);
226         if (size <= 0 || size >= sizeof(name)) {
227             size = sizeof(name) - 1;
228             if (!GetUserName(name, &size)) {
229                 KLOGEXIT( KABADARGUMENT );
230             }
231         }
232     }
233
234     if (as->parms[aPASSWORD].items) {
235         /*
236          * Current argument is the desired password string.  Remember it in
237          * our local buffer, and zero out the argument string - anyone can
238          * see it there with ps!
239          */
240         foundPassword = 1;
241         strncpy (passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
242         memset (as->parms[aPASSWORD].items->data, 0,
243                strlen(as->parms[aPASSWORD].items->data));
244     }
245
246     if (as->parms[aLIFETIME].items) {
247         char *life = as->parms[aLIFETIME].items->data;
248         char *sp;                       /* string ptr to rest of life */
249         lifetime = 3600*strtol (life, &sp, 0); /* hours */
250         if (sp == life) {
251 bad_lifetime:
252             if (!Silent) fprintf (stderr, "%s: translating '%s' to lifetime failed\n",
253                                rn, life);
254             return KABADARGUMENT;
255         }
256         if (*sp == ':') {
257             life = sp+1;                /* skip the colon */
258             lifetime += 60*strtol (life, &sp, 0); /* minutes */
259             if (sp == life) goto bad_lifetime;
260             if (*sp == ':') {
261                 life = sp+1;
262                 lifetime += strtol (life, &sp, 0); /* seconds */
263                 if (sp == life) goto bad_lifetime;
264                 if (*sp) goto bad_lifetime;
265             } else if (*sp) goto bad_lifetime;
266         } else if (*sp) goto bad_lifetime;
267         if (lifetime > MAXKTCTICKETLIFETIME) {
268             if (!Silent)
269                 fprintf (stderr,
270                 "%s: a lifetime of %.2f hours is too long, must be less than %d.\n",
271                 rn, (double)lifetime/3600.0, MAXKTCTICKETLIFETIME/3600);
272             KLOGEXIT( KABADARGUMENT );
273         }
274     } else lifetime = 0;
275
276     if (!foundExplicitCell) strcpy (realm, defaultCell);
277
278     /* Get the password if it wasn't provided. */
279     if (!foundPassword) {
280         if (Pipe) {
281             strncpy(passwd, getpipepass(), sizeof(passwd));
282         }
283         else {
284             if (read_pw_string(passwd, sizeof(passwd)))
285                 reason = "can't read password from terminal";
286             else if (strlen(passwd) == 0)
287                 reason = "zero length password is illegal";
288             else
289                 reason = NULL;
290             if (reason) {
291                 fprintf (stderr, "Unable to login because %s.\n", reason);
292                 KLOGEXIT( KABADARGUMENT );
293             }
294         }
295     }
296
297     code = ka_UserAuthenticateGeneral (KA_USERAUTH_VERSION,
298                                        name, instance, realm, passwd, lifetime,
299                                        &password_expires, 0, &reason);
300     memset (passwd, 0, sizeof(passwd));
301     if (code) {
302         if (!Silent) {
303             fprintf (stderr,
304                      "Unable to authenticate to AFS because %s.\n", reason);
305           }
306         KLOGEXIT( code );
307       }
308
309 #ifndef AFS_KERBEROS_ENV
310     if (writeTicketFile) {
311        code = krb_write_ticket_file (realm);
312        if (!Silent) {
313           if (code) 
314               com_err (rn, code, "writing Kerberos ticket file");
315           else fprintf (stderr, "Wrote ticket file to /tmp\n");
316       }
317    }
318 #endif
319  
320 #ifdef DEBUGEXPIRES
321        if (password_expires >= 0) {
322          printf ("password expires at %ld\n", password_expires);
323        }
324 #endif /* DEBUGEXPIRES */
325
326     return 0;
327 }