Windows: prevent buffer overrun in cklog
[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         if (strlen(as->parms[aCELL].items->data) >= sizeof(realm)) {
196             if (!Silent)
197                 fprintf(stderr,
198                         "Cell name too long - maximum length is %d\n",
199                         sizeof(realm) - 1);
200             return -1;
201         }
202         strncpy (realm, as->parms[aCELL].items->data, sizeof(realm));
203         realm[sizeof(realm) - 1] = '\0';
204     }
205
206     if (as->parms[aSERVERS].items) {
207         fprintf (stderr, "SERVERS option not available.\n");
208     }
209
210     if (as->parms[aPRINCIPAL].items) {
211         ka_ParseLoginName (as->parms[aPRINCIPAL].items->data,
212                            name, instance, cell);
213         if (strlen (instance) > 0)
214             if (!Silent) {
215                 fprintf (stderr,
216                          "Non-null instance (%s) may cause strange behavior.\n",
217                          instance);
218             }
219         if (strlen(cell) > 0) {
220             if (foundExplicitCell) {
221                 if (!Silent) {
222                     fprintf (stderr,
223                           "%s: May not specify an explicit cell twice.\n", rn);
224                 }
225                 return -1;
226             }
227             foundExplicitCell = 1;
228             if (strlen(cell) >= sizeof(realm)) {
229                 if (!Silent)
230                     fprintf(stderr,
231                             "Cell too long - maximum length is %d\n",
232                             sizeof(realm) - 1);
233                 return -1;
234             }
235             strncpy (realm, cell, sizeof(realm));
236             realm[sizeof(realm) - 1] = '\0';
237         }
238     } else {
239         /* No explicit name provided. */
240         DWORD size = GetEnvironmentVariable("USERNAME", name, sizeof(name) - 1);
241         if (size <= 0 || size >= sizeof(name)) {
242             size = sizeof(name) - 1;
243             if (!GetUserName(name, &size)) {
244                 KLOGEXIT( BAD_ARGUMENT );
245             }
246         }
247     }
248
249     if (as->parms[aPASSWORD].items) {
250         /*
251          * Current argument is the desired password string.  Remember it in
252          * our local buffer, and zero out the argument string - anyone can
253          * see it there with ps!
254          */
255         foundPassword = 1;
256         if (strlen(as->parms[aPASSWORD].items->data) >= sizeof(passwd)) {
257             if (!Silent)
258                 fprintf(stderr,
259                         "Password too long - maximum length is %d\n",
260                         sizeof(passwd) - 1);
261             return -1;
262         }
263         strncpy (passwd, as->parms[aPASSWORD].items->data, sizeof(passwd));
264         passwd[sizeof(passwd) - 1] = '\0';
265         memset (as->parms[aPASSWORD].items->data, 0,
266                strlen(as->parms[aPASSWORD].items->data));
267     }
268
269     if (as->parms[aLIFETIME].items) {
270         char *life = as->parms[aLIFETIME].items->data;
271         char *sp;                       /* string ptr to rest of life */
272         lifetime = 3600*strtol (life, &sp, 0); /* hours */
273         if (sp == life) {
274 bad_lifetime:
275             if (!Silent) fprintf (stderr, "%s: translating '%s' to lifetime failed\n",
276                                rn, life);
277             return BAD_ARGUMENT;
278         }
279         if (*sp == ':') {
280             life = sp+1;                /* skip the colon */
281             lifetime += 60*strtol (life, &sp, 0); /* minutes */
282             if (sp == life) goto bad_lifetime;
283             if (*sp == ':') {
284                 life = sp+1;
285                 lifetime += strtol (life, &sp, 0); /* seconds */
286                 if (sp == life) goto bad_lifetime;
287                 if (*sp) goto bad_lifetime;
288             } else if (*sp) goto bad_lifetime;
289         } else if (*sp) goto bad_lifetime;
290         if (lifetime > MAXKTCTICKETLIFETIME) {
291             if (!Silent)
292                 fprintf (stderr,
293                 "%s: a lifetime of %.2f hours is too long, must be less than %d.\n",
294                 rn, (double)lifetime/3600.0, MAXKTCTICKETLIFETIME/3600);
295             KLOGEXIT( BAD_ARGUMENT );
296         }
297     } else lifetime = 0;
298
299     if (!foundExplicitCell) strcpy (realm, defaultCell);
300
301     /* Get the password if it wasn't provided. */
302     if (!foundPassword) {
303         if (Pipe) {
304             strncpy(passwd, getpipepass(), sizeof(passwd));
305         }
306         else {
307             if (read_pw_string(passwd, sizeof(passwd)))
308                 reason = "can't read password from terminal";
309             else if (strlen(passwd) == 0)
310                 reason = "zero length password is illegal";
311             else
312                 reason = NULL;
313             if (reason) {
314                 fprintf (stderr, "Unable to login because %s.\n", reason);
315                 KLOGEXIT( BAD_ARGUMENT );
316             }
317         }
318     }
319
320     code = ka_UserAuthenticateGeneral (KA_USERAUTH_VERSION,
321                                        name, instance, realm, passwd, lifetime,
322                                        &password_expires, 0, &reason);
323     memset (passwd, 0, sizeof(passwd));
324     if (code) {
325         if (!Silent) {
326             fprintf (stderr,
327                      "Unable to authenticate to AFS because %s.\n", reason);
328           }
329         KLOGEXIT( code );
330       }
331
332 #ifndef AFS_KERBEROS_ENV
333     if (writeTicketFile) {
334        code = krb_write_ticket_file (realm);
335        if (!Silent) {
336           if (code) 
337               afs_com_err (rn, code, "writing Kerberos ticket file");
338           else fprintf (stderr, "Wrote ticket file to /tmp\n");
339       }
340    }
341 #endif
342  
343 #ifdef DEBUGEXPIRES
344        if (password_expires >= 0) {
345          printf ("password expires at %ld\n", password_expires);
346        }
347 #endif /* DEBUGEXPIRES */
348
349     return 0;
350 }