Windows global stuff:
[krb5.git] / src / lib / krb5 / krb / preauth.c
1 /*
2  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
3  * All Rights Reserved.
4  *
5  * Export of this software from the United States of America may
6  *   require a specific license from the United States Government.
7  *   It is the responsibility of any person or organization contemplating
8  *   export to obtain such a license before exporting.
9  *
10  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11  * distribute this software and its documentation for any purpose and
12  * without fee is hereby granted, provided that the above copyright
13  * notice appear in all copies and that both that copyright notice and
14  * this permission notice appear in supporting documentation, and that
15  * the name of M.I.T. not be used in advertising or publicity pertaining
16  * to distribution of the software without specific, written prior
17  * permission.  M.I.T. makes no representations about the suitability of
18  * this software for any purpose.  It is provided "as is" without express
19  * or implied warranty.
20  *
21  * Sandia National Laboratories also makes no representations about the 
22  * suitability of the modifications, or additions to this software for 
23  * any purpose.  It is provided "as is" without express or implied warranty.
24  *
25  * Note: The internal interfaces to this routine are subject to change
26  * and/or cleanup.  You should only expect the interfaces to
27  * krb5_obtain_padata and krb5_verify_padata to have some chance of
28  * staying stable.  [tytso:19920903.1544EDT]
29  */
30
31 /*
32  * This file contains routines for establishing, verifying, and any other
33  * necessary functions, for utilizing the pre-authentication field of the 
34  * kerberos kdc request, with various hardware/software verification devices.
35  *
36  * Note: At some point these functions may very well be split apart
37  * into different files.... [tytso:19920903.1618EDT]
38  */
39
40 #include <stdio.h>
41 #include <time.h>
42 #include "k5-int.h"
43 #include <syslog.h>
44 #ifdef _MSDOS
45 #define getpid _getpid
46 #include <process.h>
47 #endif
48
49 static krb5_preauth_ops preauth_systems[] = {
50     {
51         0,
52         KRB5_PADATA_ENC_UNIX_TIME,
53         KRB5_PREAUTH_FLAGS_ENCRYPT,
54         get_unixtime_padata,
55         verify_unixtime_padata,
56     },
57     {
58         0,
59         KRB5_PADATA_ENC_SANDIA_SECURID,
60         KRB5_PREAUTH_FLAGS_ENCRYPT | KRB5_PREAUTH_FLAGS_HARDWARE,
61         get_securid_padata,
62         verify_securid_padata,
63     },
64     { -1,}
65 };
66
67 static krb5_error_code find_preauthenticator
68     PROTOTYPE((int type, krb5_preauth_ops **Preauth_proc));
69
70 /*
71  *   krb5_obtain_padata  is a glue routine which when passed in
72  *   a preauthentication type, client principal, and src_addr, returns
73  *   preauthentication data contained in data to be passed onto the KDC.
74  *   
75  *   If problems occur then a non zero value is returned...
76  *
77  *   Note: This is a first crack at what any preauthentication will need...
78  */
79 krb5_error_code
80 krb5_obtain_padata(context, type, client, src_addr, encrypt_key, ret_data)
81     krb5_context context;
82     int type;                           /*IN:  Preauth type */
83     krb5_principal client;              /*IN:  requestor */
84     krb5_address **src_addr;            /*IN:  array of ptrs to addresses */
85     krb5_keyblock *encrypt_key;         /*IN:  encryption key */
86     krb5_pa_data **ret_data;                    /*OUT: Returned padata */
87 {
88     krb5_error_code     retval;
89     krb5_preauth_ops    *p_system;
90     krb5_encrypt_block  eblock;
91     krb5_data           scratch;
92     krb5_pa_data        *data;
93
94     if (!ret_data)
95         return EINVAL;
96     *ret_data = 0;
97     
98     if (type == KRB5_PADATA_NONE ) 
99         return(0);
100
101     data = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
102     if (!data)
103         return ENOMEM;
104     
105     data->length = 0;
106     data->contents = 0;
107     data->pa_type = type;
108
109     /* Find appropriate preauthenticator */
110     retval = find_preauthenticator(type, &p_system);
111     if (retval)
112         goto error_out;
113
114     retval = (*p_system->obtain)(context, client, src_addr, data );
115     if (retval)
116         goto error_out;
117
118     /* Check to see if we need to encrypt padata */
119     if (p_system->flags & KRB5_PREAUTH_FLAGS_ENCRYPT) {
120         /* If we dont have a encryption key we are out of luck */
121         if (!encrypt_key) {
122             retval = KRB5_PREAUTH_NO_KEY;
123             goto error_out;
124         }
125         krb5_use_keytype(context, &eblock, encrypt_key->keytype);
126
127         /* do any necessay key pre-processing */
128         retval = krb5_process_key(context, &eblock, encrypt_key);
129         if (retval)
130             goto error_out;
131         
132         /*
133          * Set up scratch data and length for encryption 
134          * Must allocate more space for checksum and confounder
135          * We also leave space for an uncrypted size field.
136          */
137         scratch.length = krb5_encrypt_size(data->length,
138                                            eblock.crypto_entry) + 4;
139         
140         if(!(scratch.data = malloc(scratch.length))){
141             (void) krb5_finish_key(context, &eblock);
142             retval = ENOMEM;
143             goto error_out;
144         }
145         
146         scratch.data[0] = data->length >> 24;
147         scratch.data[1] = data->length >> 16;
148         scratch.data[2] = data->length >> 8;
149         scratch.data[3] = data->length;
150
151         /* Encrypt preauth data in encryption key */
152         if (retval = krb5_encrypt(context, (krb5_pointer) data->contents,
153                                   (char *) scratch.data + 4,
154                                   data->length, &eblock, 0)) {
155             (void) krb5_finish_key(context, &eblock);
156             free(scratch.data);
157             goto error_out;
158         }
159         (void) krb5_finish_key(context, &eblock);
160             
161         free(data->contents);
162         data->length = scratch.length;
163         data->contents = (unsigned char *) scratch.data;
164     }
165
166     *ret_data = data;
167     return 0;
168     
169 error_out:
170     free(data);
171     return retval;
172 }
173
174 /*
175  *   krb5_verify_padata  is a glue routine which when passed in
176  *   the client, src_addr and padata verifies it with the appropriate 
177  *   verify function.
178  *  
179  *   If problems occur then a non zero value is returned...
180  *   else returns zero if padata verifies, and returns a "unique" id.
181  *
182  *   Note: This is a first crack at what any preauthentication will need...
183  */
184
185 krb5_error_code
186 krb5_verify_padata(context, data,client,src_addr, decrypt_key, req_id, flags)
187     krb5_context context;
188     krb5_pa_data *data;                 /*IN: padata */
189     krb5_principal client;              /*IN: requestor */
190     krb5_address **src_addr;            /*IN: array of ptrs to addresses */
191     krb5_keyblock *decrypt_key;         /*IN: decryption key */
192     int * req_id;                       /*OUT: identifier */
193     int * flags;                        /*OUT: flags  */
194 {
195     krb5_preauth_ops    *p_system;
196     krb5_encrypt_block  eblock;
197     krb5_data           scratch;
198     int                 free_scratch = 0;
199     krb5_checksum       cksum;
200     krb5_error_code     retval;
201
202     if (!data)
203         return(EINVAL);
204
205     /* Find appropriate preauthenticator */
206     retval = find_preauthenticator((int) data->pa_type, &p_system);
207     if (retval)
208         return retval;
209
210     /* Check to see if we need to decrypt padata */
211     if (p_system->flags & KRB5_PREAUTH_FLAGS_ENCRYPT) {
212
213         /* If we dont have a decryption key we are out of luck */
214         if (!decrypt_key)
215             return(EINVAL);
216
217         krb5_use_keytype(context, &eblock, decrypt_key->keytype);
218
219         scratch.length = data->length;
220         if (!(scratch.data = (char *)malloc(scratch.length))) {
221            return(ENOMEM);
222         }
223
224         /* do any necessay key pre-processing */
225         retval = krb5_process_key(context, &eblock,decrypt_key);
226         if (retval) {
227            free(scratch.data);
228            return(retval);
229         }
230
231         /* Decrypt data */
232         retval = krb5_decrypt(context, (char *) data->contents + 4,
233                               (krb5_pointer) scratch.data,
234                               scratch.length - 4, &eblock, 0);
235         if (retval) {
236            (void) krb5_finish_key(context, &eblock);
237            free(scratch.data);
238            return(retval);
239         }
240
241         scratch.length  = (((int) ((unsigned char *)data->contents)[0] << 24)
242                            + ((int) ((unsigned char *)data->contents)[1] << 16)
243                            + ((int) ((unsigned char *)data->contents)[2] << 8)
244                            + (int) ((unsigned char *)data->contents)[3]);
245         free_scratch++;
246     } else {
247         scratch.data = (char *) data->contents;
248         scratch.length = data->length;
249     }
250
251     retval = (*p_system->verify)(context, client, src_addr, &scratch);
252     if (free_scratch)
253         free(scratch.data);
254     if (retval)
255         return retval;
256     if (flags)
257         *flags = p_system->flags;
258
259     /* Generate a request id by crc32ing the (encrypted) preauth data. */
260     /* Note: The idea behind req_id is that it is dependant upon
261              the information in data. This could then be used for
262              replay detection. */
263     /* MUST malloc cksum.contents */
264     cksum.contents = (krb5_octet *)calloc(1,
265                                 krb5_checksum_size(context, CKSUMTYPE_CRC32));
266     if (!cksum.contents) return(1);
267
268     if (krb5_calculate_checksum(context, CKSUMTYPE_CRC32,
269                         data->contents,
270                         data->length,
271                         0, /* seed is ignored */
272                         0, /* seed length is ignored */
273                         &cksum )) {
274         *req_id = 0;
275     } else {
276         /* Checksum length should be 32 bits, so truncation should never
277            take place */
278         if ( cksum.length > sizeof(*req_id)) cksum.length = sizeof(*req_id);
279
280         /* Offset req_id for 64 bit systems */
281         memcpy((char *)req_id + (sizeof(*req_id) - cksum.length),
282                 cksum.contents,cksum.length);
283     } 
284     free(cksum.contents);
285     return(0);
286 }
287
288 static krb5_error_code
289 find_preauthenticator(type, preauth)
290     int                 type;
291     krb5_preauth_ops    **preauth;
292 {
293     krb5_preauth_ops *ap = preauth_systems;
294     
295     while ((ap->type != -1) && (ap->type != type))
296         ap++;
297     if (ap->type == -1)
298         return(KRB5_PREAUTH_BAD_TYPE);
299     *preauth = ap;
300     return 0;
301
302
303 /*
304  * Format is:   8 bytes of random confounder,
305  *              1 byte version number (currently 0),
306  *              4 bytes: number of seconds since Jan 1, 1970, in MSB order.
307  */
308 int seeded = 0 ; /* Used by srand below */
309
310 krb5_error_code
311 get_unixtime_padata(context, client, src_addr, pa_data)
312     krb5_context context;
313     krb5_principal client;
314     krb5_address **src_addr;
315     krb5_pa_data *pa_data;
316 {
317     unsigned char *tmp;
318     krb5_error_code     retval;
319     krb5_timestamp kdc_time;
320     int         i;
321
322     pa_data->length = 13;
323     tmp = pa_data->contents = (unsigned char *) malloc(pa_data->length);
324     if (!tmp) 
325         return(ENOMEM);
326
327     retval = krb5_timeofday(context, &kdc_time);
328     if (retval)
329         return retval;
330     if ( !seeded) {
331         seeded = (int) kdc_time + getpid();
332         srand(seeded);
333     }
334
335     for (i=0; i < 8; i++)
336         *tmp++ = rand() & 255;
337
338     *tmp++ = (unsigned char) 0;
339     *tmp++ = (unsigned char) ((kdc_time >> 24) & 255);
340     *tmp++ = (unsigned char) ((kdc_time >> 16) & 255);
341     *tmp++ = (unsigned char) ((kdc_time >> 8) & 255);
342     *tmp++ = (unsigned char) (kdc_time & 255);
343
344     return(0);
345 }
346
347 krb5_error_code
348 verify_unixtime_padata(context, client, src_addr, data)
349     krb5_context context;
350     krb5_principal client;
351     krb5_address **src_addr;
352     krb5_data *data;
353 {
354     unsigned char       *tmp;
355     krb5_error_code     retval;
356     krb5_timestamp      currenttime, patime;
357     extern krb5_deltat  krb5_clockskew;
358 #define in_clock_skew(date) (labs((date)-currenttime) < krb5_clockskew)
359
360     tmp = (unsigned char *) data->data;
361     if (tmp[8] != 0)
362         return KRB5_PREAUTH_FAILED;
363     patime = (int) tmp[9] << 24;
364     patime += (int) tmp[10] << 16;
365     patime += (int) tmp[11] << 8;
366     patime += tmp[12];
367
368     retval = krb5_timeofday(context, &currenttime);
369     if (retval)
370         return retval;
371
372     if (!in_clock_skew(patime))
373         return KRB5_PREAUTH_FAILED;
374
375     return 0;
376 }
377
378 #ifdef KRBCONF_SECUREID
379 #include "sdcli.h"
380 #include "sdconf.c"
381
382 krb5_error_code
383 verify_securid_padata(client, src_addr, data)
384     krb5_principal client;
385     krb5_address **src_addr;
386     krb5_data *data;
387 {
388    extern perform_hw;
389
390    if (perform_hw) {
391         krb5_error_code retval;
392         char username[255];
393         struct SD_CLIENT sd;
394
395         memset((char *)&sd,0, sizeof (sd));
396         memset((char *) username, 0, sizeof(username));
397         memcpy((char *) username, krb5_princ_component(context, client,0)->data,
398                                   krb5_princ_component(context, client,0)->length);
399         /* If Instance then Append */
400         if (krb5_princ_size(context, client) > 1 ) {
401             if (strncmp(krb5_princ_realm(context, client)->data,
402                         krb5_princ_component(context, client,1)->data,
403                         krb5_princ_component(context, client,1)->length) ||
404                         krb5_princ_realm(context, client)->length != 
405                         krb5_princ_component(context, client,1)->length) {
406                 strncat(username,"/",1);
407                 strncat(username,krb5_princ_component(context, client,1)->data,
408                                  krb5_princ_component(context, client,1)->length);
409             }
410         }
411         if (retval = sd_check(data->data,username,&sd) != ACM_OK) {
412                 syslog(LOG_INFO, 
413                     "%s - Invalid Securid Authentication Data sd_check Code %d",
414                         username, retval);
415                 return(KRB5_PREAUTH_FAILED);
416         }
417         return(0);
418     } else {
419         char *username = 0;
420
421         krb5_unparse_name(context, client,&username);
422         syslog(LOG_INFO, 
423             "%s Provided Securid but this KDC does not support Securid",
424                 username);
425         free(username);
426         return(KRB5_PREAUTH_FAILED);
427     }
428 }
429 #else
430 krb5_error_code
431 verify_securid_padata(context, client, src_addr, data)
432     krb5_context context;
433     krb5_principal client;
434     krb5_address **src_addr;
435     krb5_data *data;
436 {
437  char *username = 0;
438         krb5_unparse_name(context, client,&username);
439         syslog(LOG_INFO, 
440             "%s Provided Securid but this KDC does not support Securid",
441                 username);
442         free(username);
443         return(KRB5_PREAUTH_FAILED);
444 }
445
446 #endif
447
448
449 /*
450 static char *krb5_SecureId_prompt = "\nEnter Your SecurId Access Code Prepended with Your PIN\n (or a \'#\'if Your PIN is entered on the card keypad)\n or Type return <CR> if You Do NOT Use a SecurId Card: ";
451  */
452 static char *krb5_SecureId_prompt = "\nEnter Your SecurId Access Code Prepended with Your PIN\n (or a \'#\'if Your PIN is entered on the card keypad): ";
453
454 krb5_error_code
455 get_securid_padata(context, client,src_addr,pa_data)
456     krb5_context context;
457     krb5_principal client;
458     krb5_address **src_addr;
459     krb5_pa_data *pa_data;
460 {
461
462  char temp[MAX_PREAUTH_SIZE];   
463  int tempsize;
464  int retval = 0;
465
466     tempsize = sizeof(temp) - 1;
467     if (krb5_read_password(context, krb5_SecureId_prompt, 0, temp, &tempsize))
468         return(KRB5_PARSE_ILLCHAR);
469     temp[tempsize] = '\0';
470
471     if (temp[0] == '\0') 
472         return(KRB5_PARSE_ILLCHAR);
473     pa_data->length = strlen(temp) + 1;
474     pa_data->contents = (krb5_octet *) calloc(1,pa_data->length);
475     if (pa_data->contents) {
476         memcpy(pa_data->contents,temp,pa_data->length);
477         retval = 0;
478     }
479     else retval = ENOMEM;
480     memset(temp,0,pa_data->length);
481     return(retval);
482 }