Disable SSL_peek until it is fixed.
[openssl.git] / ssl / kssl.c
1 /* ssl/kssl.c -*- mode: C; c-file-style: "eay" -*- */
2 /* Written by Vern Staats <staatsvr@asc.hpc.mil> for the OpenSSL project 2000.
3  */
4 /* ====================================================================
5  * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer. 
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. All advertising materials mentioning features or use of this
20  *    software must display the following acknowledgment:
21  *    "This product includes software developed by the OpenSSL Project
22  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
23  *
24  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
25  *    endorse or promote products derived from this software without
26  *    prior written permission. For written permission, please contact
27  *    licensing@OpenSSL.org.
28  *
29  * 5. Products derived from this software may not be called "OpenSSL"
30  *    nor may "OpenSSL" appear in their names without prior written
31  *    permission of the OpenSSL Project.
32  *
33  * 6. Redistributions of any form whatsoever must retain the following
34  *    acknowledgment:
35  *    "This product includes software developed by the OpenSSL Project
36  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
39  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
41  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
42  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
47  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
49  * OF THE POSSIBILITY OF SUCH DAMAGE.
50  * ====================================================================
51  *
52  * This product includes cryptographic software written by Eric Young
53  * (eay@cryptsoft.com).  This product includes software written by Tim
54  * Hudson (tjh@cryptsoft.com).
55  *
56  */
57
58
59 /*      ssl/kssl.c  --  Routines to support (& debug) Kerberos5 auth for openssl
60 **
61 **      19990701        VRS     Started.
62 */
63
64 #ifndef NO_KRB5
65 #include <string.h>
66 #include <openssl/ssl.h>
67
68 char
69 *kstring(char *string)
70         {
71         static char     *null = "[NULL]";
72
73         return ((string == NULL)? null: string);
74         }
75
76 #define MAXKNUM 255
77 char
78 *knumber(int len, krb5_octet *contents)
79         {
80         static char     buf[MAXKNUM+1];
81         int             i;
82
83         BIO_snprintf(buf, MAXKNUM, "[%d] ", len);
84
85         for (i=0; i < len  &&  MAXKNUM > strlen(buf)+3; i++)
86                 {
87                 BIO_snprintf(&buf[strlen(buf)], 3, "%02x", contents[i]);
88                 }
89
90         return (buf);
91 }
92
93
94 /*      Set kssl_err error info when reason text is a simple string
95 **              kssl_err = struct { int reason; char text[KSSL_ERR_MAX+1]; }
96 */
97 void
98 kssl_err_set(KSSL_ERR *kssl_err, int reason, char *text)
99         {
100         if (kssl_err == NULL)  return;
101
102         kssl_err->reason = reason;
103         BIO_snprintf(kssl_err->text, KSSL_ERR_MAX, text);
104         return;
105         }
106
107
108 /*      Display contents of krb5_data struct, for debugging
109 */
110 void
111 print_krb5_data(char *label, krb5_data *kdata)
112         {
113         int     i;
114
115         printf("%s[%d] ", label, kdata->length);
116         for (i=0; i < kdata->length; i++)
117                 {
118                 if (isprint((int) kdata->data[i]))
119                         printf( "%c ",  kdata->data[i]);
120                 else
121                         printf( "%02x", kdata->data[i]);
122                 }
123         printf("\n");
124         }
125
126
127 /*      Display contents of krb5_authdata struct, for debugging
128 */
129 void
130 print_krb5_authdata(char *label, krb5_authdata **adata)
131         {
132         if (adata == NULL)
133                 {
134                 printf("%s, authdata==0\n", label);
135                 return;
136                 }
137         printf("%s [%p]\n", label, adata);
138 #if 0
139         {
140         int     i;
141         printf("%s[at%d:%d] ", label, adata->ad_type, adata->length);
142         for (i=0; i < adata->length; i++)
143                 {
144                 printf((isprint(adata->contents[i]))? "%c ": "%02x",
145                         adata->contents[i]);
146                 }
147         printf("\n");
148         }
149 #endif
150 }
151
152
153 /*      Display contents of krb5_keyblock struct, for debugging
154 */
155 void
156 print_krb5_keyblock(char *label, krb5_keyblock *keyblk)
157         {
158         int     i;
159
160         if (keyblk == NULL)
161                 {
162                 printf("%s, keyblk==0\n", label);
163                 return;
164                 }
165         printf("%s\n\t[et%d:%d]: ", label, keyblk->enctype, keyblk->length);
166         for (i=0; i < keyblk->length; i++)
167                 {
168                 printf("%02x",keyblk->contents[i]);
169                 }
170         printf("\n");
171         }
172
173
174 /*      Given krb5 service (typically "kssl") and hostname in kssl_ctx,
175 **      Create Kerberos AP_REQ message for SSL Client.
176 **
177 **      19990628        VRS     Started.
178 */
179 krb5_error_code
180 kssl_cget_tkt(  /* UPDATE */    KSSL_CTX *kssl_ctx,
181                 /* OUT    */    krb5_data *krb5_app_req, KSSL_ERR *kssl_err)
182 {
183         krb5_error_code         krb5rc = KRB5KRB_ERR_GENERIC;
184         krb5_context            krb5context = NULL;
185         krb5_auth_context       krb5auth_context = NULL;
186         krb5_ccache             krb5ccdef = NULL;
187         krb5_creds              krb5creds, *krb5credsp = NULL;
188         krb5_data               krb5in_data;
189
190         kssl_err_set(kssl_err, 0, "");
191         memset((char *)&krb5creds, 0, sizeof(krb5creds));
192
193         if (!kssl_ctx)
194                 {
195                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
196                         "No kssl_ctx defined.\n");
197                 goto err;
198                 }
199         else if (!kssl_ctx->service_host)
200                 {
201                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
202                         "kssl_ctx service_host undefined.\n");
203                 goto err;
204                 }
205
206         if ((krb5rc = krb5_init_context(&krb5context)) != 0)
207                 {
208                 BIO_snprintf(kssl_err->text,KSSL_ERR_MAX,
209                         "krb5_init_context() fails: %d\n", krb5rc);
210                 kssl_err->reason = SSL_R_KRB5_C_INIT;
211                 goto err;
212                 }
213
214         if ((krb5rc = krb5_sname_to_principal(krb5context,
215                 kssl_ctx->service_host,
216                 (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC,
217                 KRB5_NT_SRV_HST, &krb5creds.server)) != 0)
218                 {
219                 BIO_snprintf(kssl_err->text,KSSL_ERR_MAX,
220                         "krb5_sname_to_principal() fails for %s/%s\n",
221                         kssl_ctx->service_host,
222                         (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC);
223                 kssl_err->reason = SSL_R_KRB5_C_INIT;
224                 goto err;
225                 }
226
227         if ((krb5rc = krb5_cc_default(krb5context, &krb5ccdef)) != 0)
228                 {
229                 kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC,
230                         "krb5_cc_default fails.\n");
231                 goto err;
232                 }
233
234         if ((krb5rc = krb5_cc_get_principal(krb5context, krb5ccdef,
235                 &krb5creds.client)) != 0)
236                 {
237                 kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC,
238                         "krb5_cc_get_principal() fails.\n");
239                 goto err;
240                 }
241
242         if ((krb5rc = krb5_get_credentials(krb5context, 0, krb5ccdef,
243                 &krb5creds, &krb5credsp)) != 0)
244                 {
245                 kssl_err_set(kssl_err, SSL_R_KRB5_C_GET_CRED,
246                         "krb5_get_credentials() fails.\n");
247                 goto err;
248                 }
249
250         krb5in_data.data = NULL;
251         krb5in_data.length = 0;
252
253         krb5rc = KRB5KRB_ERR_GENERIC;
254         /*      caller should free data of krb5_app_req  */
255         if ((krb5rc = krb5_mk_req_extended(krb5context, &krb5auth_context,
256                 0, &krb5in_data, krb5credsp, krb5_app_req)) != 0)
257                 {
258                 kssl_err_set(kssl_err, SSL_R_KRB5_C_MK_REQ,
259                         "krb5_mk_req_extended() fails.\n");
260                 goto err;
261                 }
262         else if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->keyblock))
263                 {
264                 kssl_err_set(kssl_err, SSL_R_KRB5_C_INIT,
265                         "kssl_ctx_setkey() fails.\n");
266                 }
267         else    krb5rc = 0;
268
269  err:
270 #ifdef KSSL_DEBUG
271         kssl_ctx_show(kssl_ctx);
272 #endif  /* KSSL_DEBUG */
273
274         if (krb5creds.client)   krb5_free_principal(krb5context, krb5creds.client);
275         if (krb5creds.server)   krb5_free_principal(krb5context, krb5creds.server);
276         if (krb5auth_context)   krb5_auth_con_free(krb5context, krb5auth_context);
277         if (krb5context)        krb5_free_context(krb5context);
278         return (krb5rc);
279 }
280
281
282 /*      Given krb5 service name in KSSL_CTX *kssl_ctx (typically "kssl"),
283 **              and krb5 AP_REQ message & message length,
284 **      Return Kerberos session key and client principle
285 **              to SSL Server in KSSL_CTX *kssl_ctx.
286 **
287 **      19990702        VRS     Started.
288 */
289 krb5_error_code
290 kssl_sget_tkt(  /* UPDATE */    KSSL_CTX *kssl_ctx,
291                 /* IN     */    char *msg, int msglen,
292                 /* OUT    */    KSSL_ERR *kssl_err  )
293         {
294         krb5_error_code                 krb5rc = KRB5KRB_ERR_GENERIC;
295         static krb5_context             krb5context = NULL;
296         static krb5_auth_context        krb5auth_context = NULL;
297         krb5_ticket                     *krb5ticket = NULL;
298         krb5_keytab                     krb5keytab = NULL;
299         krb5_principal                  krb5server;
300         krb5_data                       krb5in_data;
301         krb5_flags                      ap_option;
302
303         kssl_err_set(kssl_err, 0, "");
304
305         if (!kssl_ctx)
306                 {
307                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "No kssl_ctx defined.\n");
308                 goto err;
309                 }
310
311 #ifdef KSSL_DEBUG
312         printf("in kssl_sget_tkt(%s)\n", kstring(kssl_ctx->service_name));
313 #endif  /* KSSL_DEBUG */
314
315         if (!krb5context  &&  (krb5rc = krb5_init_context(&krb5context)))
316                 {
317                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
318                         "krb5_init_context() fails.\n");
319                 goto err;
320                 }
321         if (krb5auth_context  &&
322                 (krb5rc = krb5_auth_con_free(krb5context, krb5auth_context)))
323                 {
324                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
325                         "krb5_auth_con_free() fails.\n");
326                 goto err;
327                 }
328         else  krb5auth_context = NULL;
329         if (!krb5auth_context  &&
330                 (krb5rc = krb5_auth_con_init(krb5context, &krb5auth_context)))
331                 {
332                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
333                         "krb5_auth_con_init() fails.\n");
334                 goto err;
335                 }
336
337         if ((krb5rc = krb5_sname_to_principal(krb5context, NULL,
338                 (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC,
339                 KRB5_NT_SRV_HST, &krb5server)) != 0)
340                 {
341                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
342                         "krb5_sname_to_principal() fails.\n");
343                 goto err;
344                 }
345
346         if (kssl_ctx->keytab_file  &&
347                 ((krb5rc = krb5_kt_resolve(krb5context, kssl_ctx->keytab_file,
348                         &krb5keytab))))
349                 {
350                 kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_kt_resolve() fails.\n");
351                 goto err;
352                 }
353         /*      kssl_ctx->keytab_file == NULL ==> use Kerberos default /etc/krb5.keytab
354         */
355
356         /*      Actual Kerberos5 krb5_recvauth() has initial conversation here
357         **      o       check KRB5_SENDAUTH_BADAUTHVERS unless KRB5_RECVAUTH_SKIP_VERSION
358         **      o       check KRB5_SENDAUTH_BADAPPLVERS
359         **      o       send "0" msg if all OK
360         */
361
362         krb5in_data.data = msg;
363         krb5in_data.length = msglen;
364         if ((krb5rc = krb5_rd_req(krb5context, &krb5auth_context, &krb5in_data,
365                 krb5server, krb5keytab, &ap_option, &krb5ticket))
366                 != 0)
367                 {
368                 BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
369                         "krb5_rd_req() fails with %x.\n", krb5rc);
370                 kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
371                 goto err;
372                 }
373
374         krb5rc = KRB5_NO_TKT_SUPPLIED;
375         if (!krb5ticket  ||     !krb5ticket->enc_part2  ||
376                 !krb5ticket->enc_part2->client  ||
377                 !krb5ticket->enc_part2->client->data  ||
378                 !krb5ticket->enc_part2->session)
379                 {
380                 kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
381                         "bad ticket from krb5_rd_req.\n");
382                 }
383         else if (kssl_ctx_setprinc(kssl_ctx, KSSL_CLIENT,
384                 &krb5ticket->enc_part2->client->realm,
385                 krb5ticket->enc_part2->client->data))
386                 {
387                 kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
388                         "kssl_ctx_setprinc() fails.\n");
389                 }
390         else if (kssl_ctx_setkey(kssl_ctx, krb5ticket->enc_part2->session))
391                 {
392                 kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
393                         "kssl_ctx_setkey() fails.\n");
394                 }
395         else    krb5rc = 0;
396
397  err:
398 #ifdef KSSL_DEBUG
399         kssl_ctx_show(kssl_ctx);
400 #endif  /* KSSL_DEBUG */
401
402         if (krb5ticket)         krb5_free_ticket(krb5context, krb5ticket);
403         if (krb5server)         krb5_free_principal(krb5context, krb5server);
404         return (krb5rc);
405         }
406
407
408 /*      Allocate & return a new kssl_ctx struct.
409 */
410 KSSL_CTX        *
411 kssl_ctx_new(void)
412         {
413         return ((KSSL_CTX *) calloc(1, sizeof(KSSL_CTX)));
414         }
415
416
417 /*      Frees a kssl_ctx struct and any allocated memory it holds.
418 **      Returns NULL.
419 */
420 KSSL_CTX        *
421 kssl_ctx_free(KSSL_CTX *kssl_ctx)
422         {
423         if (kssl_ctx == NULL)  return kssl_ctx;
424
425         if (kssl_ctx->key)              memset(kssl_ctx->key, 0, kssl_ctx->length);
426         if (kssl_ctx->key)              free(kssl_ctx->key);
427         if (kssl_ctx->client_princ)     free(kssl_ctx->client_princ);
428         if (kssl_ctx->service_host)     free(kssl_ctx->service_host);
429         if (kssl_ctx->service_name)     free(kssl_ctx->service_name);
430         if (kssl_ctx->keytab_file)      free(kssl_ctx->keytab_file);
431
432         free(kssl_ctx);
433         return (KSSL_CTX *) NULL;
434         }
435
436
437 /*      Given a (krb5_data *) entity (and optional realm),
438 **      set the plain (char *) client_princ or service_host member
439 **      of the kssl_ctx struct.
440 */
441 krb5_error_code
442 kssl_ctx_setprinc(KSSL_CTX *kssl_ctx, int which,
443         krb5_data *realm, krb5_data *entity)
444         {
445         char    **princ;
446         int     length;
447
448         if (kssl_ctx == NULL  ||  entity == NULL)  return KSSL_CTX_ERR;
449
450         switch (which)
451                 {
452         case KSSL_CLIENT:       princ = &kssl_ctx->client_princ;        break;
453         case KSSL_SERVER:       princ = &kssl_ctx->service_host;        break;
454         default:                return KSSL_CTX_ERR;                    break;
455                 }
456         if (*princ)  free(*princ);
457
458         length = entity->length + ((realm)? realm->length + 2: 1);
459         if ((*princ = calloc(1, length)) == NULL)       return KSSL_CTX_ERR;
460         else
461                 {
462                 strncpy(*princ, entity->data, entity->length);
463                 if (realm)
464                         {
465                         strcat (*princ, "@");
466                         (void) strncat(*princ, realm->data, realm->length);
467                         }
468                 }
469
470         return KSSL_CTX_OK;
471         }
472
473
474 /*      Set one of the plain (char *) string members of the kssl_ctx struct.
475 **      Default values should be:
476 **              which == KSSL_SERVICE   =>      "kssl" (KRB5SVC)
477 **              which == KSSL_KEYTAB    =>      "/etc/krb5.keytab.kssl" (KRB5KEYTAB)
478 */
479 krb5_error_code
480 kssl_ctx_setstring(KSSL_CTX *kssl_ctx, int which, char *text)
481         {
482         char    **string;
483
484         if (!kssl_ctx)  return KSSL_CTX_ERR;
485
486         switch (which)
487                 {
488         case KSSL_SERVICE:      string = &kssl_ctx->service_name;       break;
489         case KSSL_SERVER:       string = &kssl_ctx->service_host;       break;
490         case KSSL_CLIENT:       string = &kssl_ctx->client_princ;       break;
491         case KSSL_KEYTAB:       string = &kssl_ctx->keytab_file;        break;
492         default:                return KSSL_CTX_ERR;                    break;
493                 }
494         if (*string)  free(*string);
495
496         if (!text)
497                 {
498                 *string = '\0';
499                 return KSSL_CTX_OK;
500                 }
501
502         if ((*string = calloc(1, strlen(text) + 1)) == NULL)  return KSSL_CTX_ERR;
503         else    strcpy(*string, text);
504
505         return KSSL_CTX_OK;
506         }
507
508
509 /*      Copy the Kerberos session key from a (krb5_keyblock *) to a kssl_ctx
510 **      struct.  Clear kssl_ctx->key if Kerberos session key is NULL.
511 */
512 krb5_error_code
513 kssl_ctx_setkey(KSSL_CTX *kssl_ctx, krb5_keyblock *session)
514         {
515         if (!kssl_ctx)  return KSSL_CTX_ERR;
516
517         if (kssl_ctx->key)
518                 {
519                 memset(kssl_ctx->key, 0, kssl_ctx->length);
520                 free(kssl_ctx->key);
521                 }
522
523         if (session)
524                 {
525                 kssl_ctx->enctype = session->enctype;
526                 kssl_ctx->length  = session->length;
527                 }
528         else
529                 {
530                 kssl_ctx->enctype = ENCTYPE_UNKNOWN;
531                 kssl_ctx->length  = 0;
532                 return KSSL_CTX_OK;
533                 }
534
535         if ((kssl_ctx->key =
536                 (krb5_octet FAR *) calloc(1, kssl_ctx->length)) == NULL)
537                 {
538                 kssl_ctx->length  = 0;
539                 return KSSL_CTX_ERR;
540                 }
541         else    memcpy(kssl_ctx->key, session->contents, session->length);
542
543         return KSSL_CTX_OK;
544         }
545
546
547 /*      Display contents of kssl_ctx struct
548 */
549 void
550 kssl_ctx_show(KSSL_CTX *kssl_ctx)
551         {
552         int     i;
553
554         printf("kssl_ctx: ");
555         if (kssl_ctx == NULL)
556                 {
557                 printf("NULL\n");
558                 return;
559                 }
560         else    printf("%p\n", kssl_ctx);
561
562         printf("\tservice:\t%s\n",
563                 (kssl_ctx->service_name)? kssl_ctx->service_name: "NULL");
564         printf("\tclient:\t%s\n",
565                 (kssl_ctx->client_princ)? kssl_ctx->client_princ: "NULL");
566         printf("\tserver:\t%s\n",
567                 (kssl_ctx->service_host)? kssl_ctx->service_host: "NULL");
568         printf("\tkeytab:\t%s\n",
569                 (kssl_ctx->keytab_file)? kssl_ctx->keytab_file: "NULL");
570         printf("\tkey [%d:%d]:\t",
571                 kssl_ctx->enctype, kssl_ctx->length);
572
573         for (i=0; i < kssl_ctx->length  &&  kssl_ctx->key; i++)
574                 {
575                 printf("%02x", kssl_ctx->key[i]);
576                 }
577         printf("\n");
578         return;
579         }
580
581 #endif  /* NO_KRB5      */
582