build the trunk on Windows (again)
[krb5.git] / src / lib / krb5 / krb / init_ctx.c
index 00bf2d528aab8559af56bfac8bf9e5d03d77effe..b80fd50fa170409ef0b116da5709b0dab9165a3d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lib/krb5/krb/init_ctx.c
  *
- * Copyright 1994 by the Massachusetts Institute of Technology.
+ * Copyright 1994,1999,2000, 2002, 2003  by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * this permission notice appear in supporting documentation, and that
  * the name of M.I.T. not be used in advertising or publicity pertaining
  * to distribution of the software without specific, written prior
- * permission.  M.I.T. makes no representations about the suitability of
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
  * this software for any purpose.  It is provided "as is" without express
  * or implied warranty.
  *
  * krb5_init_contex()
  */
 
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ * 
+ * All rights reserved.
+ * 
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government.  It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  FundsXpress makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
 #include "k5-int.h"
+#include <ctype.h>
+#include "brand.c"
+/* There has to be a better way for windows... */
+#if defined(unix) || TARGET_OS_MAC
+#include "../krb5_libinit.h"
+#endif
+
+/* The des-mdX entries are last for now, because it's easy to
+   configure KDCs to issue TGTs with des-mdX keys and then not accept
+   them.  This'll be fixed, but for better compatibility, let's prefer
+   des-crc for now.  */
+#define DEFAULT_ETYPE_LIST     \
+       "aes256-cts-hmac-sha1-96 " \
+       "aes128-cts-hmac-sha1-96 " \
+       "des3-cbc-sha1 arcfour-hmac-md5 " \
+       "des-cbc-crc des-cbc-md5 des-cbc-md4 "
+
+/* Not included:
+       "aes128-cts-hmac-sha1-96 " \
+ */
+
+#if (defined(_WIN32))
+extern krb5_error_code krb5_vercheck();
+extern void krb5_win_ccdll_load(krb5_context context);
+#endif
+
+static krb5_error_code init_common (krb5_context *, krb5_boolean, krb5_boolean);
 
-krb5_error_code INTERFACE
-krb5_init_context(context)
-       krb5_context *context;
+krb5_error_code KRB5_CALLCONV
+krb5_init_context(krb5_context *context)
 {
-       krb5_context ctx;
+
+       return init_common (context, FALSE, FALSE);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_init_secure_context(krb5_context *context)
+{
+
+        /* This is to make gcc -Wall happy */
+        if(0) krb5_brand[0] = krb5_brand[0];
+       return init_common (context, TRUE, FALSE);
+}
+
+krb5_error_code
+krb5int_init_context_kdc(krb5_context *context)
+{
+    return init_common (context, FALSE, TRUE);
+}
+
+static krb5_error_code
+init_common (krb5_context *context, krb5_boolean secure, krb5_boolean kdc)
+{
+       krb5_context ctx = 0;
        krb5_error_code retval;
+       struct {
+           krb5_int32 now, now_usec;
+           long pid;
+       } seed_data;
+       krb5_data seed;
+       int tmp;
+
+       /* Verify some assumptions.  If the assumptions hold and the
+          compiler is optimizing, this should result in no code being
+          executed.  If we're guessing "unsigned long long" instead
+          of using uint64_t, the possibility does exist that we're
+          wrong.  */
+       {
+           krb5_ui_8 i64;
+           assert(sizeof(i64) == 8);
+           i64 = 0, i64--, i64 >>= 62;
+           assert(i64 == 3);
+           i64 = 1, i64 <<= 31, i64 <<= 31, i64 <<= 1;
+           assert(i64 != 0);
+           i64 <<= 1;
+           assert(i64 == 0);
+       }
+
+       retval = krb5int_initialize_library();
+       if (retval)
+           return retval;
+
+#if (defined(_WIN32))
+       /* 
+        * Load the krbcc32.dll if necessary.  We do this here so that
+        * we know to use API: later on during initialization.
+        * The context being NULL is ok.
+        */
+       krb5_win_ccdll_load(ctx);
+
+       /*
+        * krb5_vercheck() is defined in win_glue.c, and this is
+        * where we handle the timebomb and version server checks.
+        */
+       retval = krb5_vercheck();
+       if (retval)
+               return retval;
+#endif
 
        *context = 0;
 
@@ -40,16 +160,85 @@ krb5_init_context(context)
        memset(ctx, 0, sizeof(struct _krb5_context));
        ctx->magic = KV5M_CONTEXT;
 
+       ctx->profile_secure = secure;
+
        /* Set the default encryption types, possible defined in krb5/conf */
-       if ((retval = krb5_set_default_in_tkt_etypes(ctx, NULL)))
+       if ((retval = krb5_set_default_in_tkt_ktypes(ctx, NULL)))
                goto cleanup;
 
-       if ((retval = krb5_os_init_context(ctx)))
+       if ((retval = krb5_set_default_tgs_ktypes(ctx, NULL)))
+               goto cleanup;
+
+       if ((retval = krb5_os_init_context(ctx, kdc)))
+               goto cleanup;
+
+       /* initialize the prng (not well, but passable) */
+       if ((retval = krb5_c_random_os_entropy( ctx, 0, NULL)) !=0)
+         goto cleanup;
+       if ((retval = krb5_crypto_us_timeofday(&seed_data.now, &seed_data.now_usec)))
+               goto cleanup;
+       seed_data.pid = getpid ();
+       seed.length = sizeof(seed_data);
+       seed.data = (char *) &seed_data;
+       if ((retval = krb5_c_random_add_entropy(ctx, KRB5_C_RANDSOURCE_TIMING, &seed)))
                goto cleanup;
-       
 
        ctx->default_realm = 0;
+       profile_get_integer(ctx->profile, "libdefaults", "clockskew",
+                           0, 5 * 60, &tmp);
+       ctx->clockskew = tmp;
+
+#if 0
+       /* Default ticket lifetime is currently not supported */
+       profile_get_integer(ctx->profile, "libdefaults", "tkt_lifetime",
+                           0, 10 * 60 * 60, &tmp);
+       ctx->tkt_lifetime = tmp;
+#endif
+
+       /* DCE 1.1 and below only support CKSUMTYPE_RSA_MD4 (2)  */
+       /* DCE add kdc_req_checksum_type = 2 to krb5.conf */
+       profile_get_integer(ctx->profile, "libdefaults",
+                           "kdc_req_checksum_type", 0, CKSUMTYPE_RSA_MD5, 
+                           &tmp);
+       ctx->kdc_req_sumtype = tmp;
+
+       profile_get_integer(ctx->profile, "libdefaults",
+                           "ap_req_checksum_type", 0, CKSUMTYPE_RSA_MD5,
+                           &tmp);
+       ctx->default_ap_req_sumtype = tmp;
 
+       profile_get_integer(ctx->profile, "libdefaults",
+                           "safe_checksum_type", 0,
+                           CKSUMTYPE_RSA_MD5_DES, &tmp);
+       ctx->default_safe_sumtype = tmp;
+
+       profile_get_integer(ctx->profile, "libdefaults",
+                           "kdc_default_options", 0,
+                           KDC_OPT_RENEWABLE_OK, &tmp);
+       ctx->kdc_default_options = tmp;
+#define DEFAULT_KDC_TIMESYNC 1
+       profile_get_integer(ctx->profile, "libdefaults",
+                           "kdc_timesync", 0, DEFAULT_KDC_TIMESYNC,
+                           &tmp);
+       ctx->library_options = tmp ? KRB5_LIBOPT_SYNC_KDCTIME : 0;
+
+       /*
+        * We use a default file credentials cache of 3.  See
+        * lib/krb5/krb/ccache/file/fcc.h for a description of the
+        * credentials cache types.
+        *
+        * Note: DCE 1.0.3a only supports a cache type of 1
+        *      DCE 1.1 supports a cache type of 2.
+        */
+#define DEFAULT_CCACHE_TYPE 4
+       profile_get_integer(ctx->profile, "libdefaults", "ccache_type",
+                           0, DEFAULT_CCACHE_TYPE, &tmp);
+       ctx->fcc_default_format = tmp + 0x0500;
+       ctx->scc_default_format = tmp + 0x0500;
+       ctx->prompt_types = 0;
+       ctx->use_conf_ktypes = 0;
+
+       ctx->udp_pref_limit = -1;
        *context = ctx;
        return 0;
 
@@ -58,84 +247,333 @@ cleanup:
        return retval;
 }
 
-void
-krb5_free_context(ctx)
-       krb5_context    ctx;
+void KRB5_CALLCONV
+krb5_free_context(krb5_context ctx)
 {
      krb5_os_free_context(ctx);
 
-     if (ctx->etypes)
-          free(ctx->etypes);
+     if (ctx->in_tkt_ktypes) {
+          free(ctx->in_tkt_ktypes);
+         ctx->in_tkt_ktypes = 0;
+     }
 
-     if (ctx->default_realm)
+     if (ctx->tgs_ktypes) {
+          free(ctx->tgs_ktypes);
+         ctx->tgs_ktypes = 0;
+     }
+
+     if (ctx->default_realm) {
          free(ctx->default_realm);
+         ctx->default_realm = 0;
+     }
+
+     if (ctx->ser_ctx_count && ctx->ser_ctx) {
+         free(ctx->ser_ctx);
+         ctx->ser_ctx = 0;
+     }
 
-     if (ctx->ser_ctx_count && ctx->ser_ctx)
-        free(ctx->ser_ctx);
+     krb5_clear_error_message(ctx);
 
      ctx->magic = 0;
      free(ctx);
 }
 
 /*
- * Set the desired default etypes, making sure they are valid.
+ * Set the desired default ktypes, making sure they are valid.
  */
 krb5_error_code
-krb5_set_default_in_tkt_etypes(context, etypes)
-       krb5_context context;
-       const krb5_enctype *etypes;
+krb5_set_default_in_tkt_ktypes(krb5_context context, const krb5_enctype *ktypes)
 {
-    krb5_enctype * new_etypes;
+    krb5_enctype * new_ktypes;
     int i;
 
-    if (etypes) {
-       for (i = 0; etypes[i]; i++) {
-           if (!valid_etype(etypes[i])) 
+    if (ktypes) {
+       for (i = 0; ktypes[i]; i++) {
+           if (!krb5_c_valid_enctype(ktypes[i])) 
                return KRB5_PROG_ETYPE_NOSUPP;
        }
 
-       /* Now copy the default etypes into the context pointer */
-       if ((new_etypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i)))
-           memcpy(new_etypes, etypes, sizeof(krb5_enctype) * i);
+       /* Now copy the default ktypes into the context pointer */
+       if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i)))
+           memcpy(new_ktypes, ktypes, sizeof(krb5_enctype) * i);
        else
            return ENOMEM;
 
     } else {
-       i = 2;
+       i = 0;
+       new_ktypes = 0;
+    }
 
-       /* Should reset the list to the runtime defaults */
-       if ((new_etypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i))) {
-           new_etypes[0] = ETYPE_DES_CBC_MD5;
-           new_etypes[1] = ETYPE_DES_CBC_CRC;
+    if (context->in_tkt_ktypes) 
+        free(context->in_tkt_ktypes);
+    context->in_tkt_ktypes = new_ktypes;
+    context->in_tkt_ktype_count = i;
+    return 0;
+}
+
+static krb5_error_code
+get_profile_etype_list(krb5_context context, krb5_enctype **ktypes, char *profstr,
+                      unsigned int ctx_count, krb5_enctype *ctx_list)
+{
+    krb5_enctype *old_ktypes;
+
+    if (ctx_count) {
+       /* application-set defaults */
+       if ((old_ktypes = 
+            (krb5_enctype *)malloc(sizeof(krb5_enctype) *
+                                   (ctx_count + 1)))) {
+           memcpy(old_ktypes, ctx_list, sizeof(krb5_enctype) * ctx_count);
+           old_ktypes[ctx_count] = 0;
        } else {
            return ENOMEM;
        }
+    } else {
+        /*
+          XXX - For now, we only support libdefaults
+          Perhaps this should be extended to allow for per-host / per-realm
+          session key types.
+        */
+
+       char *retval;
+       char *sp, *ep;
+       int i, j, count;
+       krb5_error_code code;
+
+       code = profile_get_string(context->profile, "libdefaults", profstr,
+                                 NULL, DEFAULT_ETYPE_LIST, &retval);
+       if (code)
+           return code;
+
+       count = 0;
+       sp = retval;
+       while (*sp) {
+           for (ep = sp; *ep && (*ep != ',') && !isspace((int) (*ep)); ep++)
+               ;
+           if (*ep) {
+               *ep++ = '\0';
+               while (isspace((int) (*ep)) || *ep == ',')
+                   *ep++ = '\0';
+           }
+           count++;
+           sp = ep;
+       }
+       
+       if ((old_ktypes =
+            (krb5_enctype *)malloc(sizeof(krb5_enctype) * (count + 1))) ==
+           (krb5_enctype *) NULL)
+           return ENOMEM;
+       
+       sp = retval;
+       j = 0;
+       i = 1;
+       while (1) {
+           if (! krb5_string_to_enctype(sp, &old_ktypes[j]))
+               j++;
+
+           if (i++ >= count)
+               break;
+
+           /* skip to next token */
+           while (*sp) sp++;
+           while (! *sp) sp++;
+       }
+
+       old_ktypes[j] = (krb5_enctype) 0;
+       profile_release_string(retval);
+    }
+
+    if (old_ktypes[0] == 0) {
+       free (old_ktypes);
+       *ktypes = 0;
+       return KRB5_CONFIG_ETYPE_NOSUPP;
     }
 
-    if (context->etypes) 
-        free(context->etypes);
-    context->etypes = new_etypes;
-    context->etype_count = i;
+    *ktypes = old_ktypes;
     return 0;
 }
 
 krb5_error_code
-krb5_get_default_in_tkt_etypes(context, etypes)
-    krb5_context context;
-    krb5_enctype **etypes;
+krb5_get_default_in_tkt_ktypes(krb5_context context, krb5_enctype **ktypes)
 {
-    krb5_enctype * old_etypes;
+    return(get_profile_etype_list(context, ktypes, "default_tkt_enctypes",
+                                 context->in_tkt_ktype_count,
+                                 context->in_tkt_ktypes));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_set_default_tgs_enctypes (krb5_context context, const krb5_enctype *ktypes)
+{
+    krb5_enctype * new_ktypes;
+    int i;
+
+    if (ktypes) {
+       for (i = 0; ktypes[i]; i++) {
+           if (!krb5_c_valid_enctype(ktypes[i])) 
+               return KRB5_PROG_ETYPE_NOSUPP;
+       }
+
+       /* Now copy the default ktypes into the context pointer */
+       if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i)))
+           memcpy(new_ktypes, ktypes, sizeof(krb5_enctype) * i);
+       else
+           return ENOMEM;
 
-    if ((old_etypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) *
-                                            (context->etype_count + 1)))) {
-       memcpy(old_etypes, context->etypes, sizeof(krb5_enctype) * 
-                               context->etype_count);
-       old_etypes[context->etype_count] = 0;
     } else {
-       return ENOMEM;
+       i = 0;
+       new_ktypes = (krb5_enctype *)NULL;
     }
 
-    *etypes = old_etypes;
+    if (context->tgs_ktypes) 
+        krb5_free_ktypes(context, context->tgs_ktypes);
+    context->tgs_ktypes = new_ktypes;
+    context->tgs_ktype_count = i;
     return 0;
-       
+}
+
+krb5_error_code krb5_set_default_tgs_ktypes
+(krb5_context context, const krb5_enctype *etypes)
+{
+  return (krb5_set_default_tgs_enctypes (context, etypes));
+}
+
+
+void
+KRB5_CALLCONV
+krb5_free_ktypes (krb5_context context, krb5_enctype *val)
+{
+    free (val);
+}
+
+krb5_error_code
+KRB5_CALLCONV
+krb5_get_tgs_ktypes(krb5_context context, krb5_const_principal princ, krb5_enctype **ktypes)
+{
+    if (context->use_conf_ktypes)
+       /* This one is set *only* by reading the config file; it's not
+          set by the application.  */
+       return(get_profile_etype_list(context, ktypes, "default_tgs_enctypes",
+                                     0, NULL));
+    else
+       return(get_profile_etype_list(context, ktypes, "default_tgs_enctypes",
+                                     context->tgs_ktype_count,
+                                     context->tgs_ktypes));
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_permitted_enctypes(krb5_context context, krb5_enctype **ktypes)
+{
+    return(get_profile_etype_list(context, ktypes, "permitted_enctypes",
+                                 context->tgs_ktype_count,
+                                 context->tgs_ktypes));
+}
+
+krb5_boolean
+krb5_is_permitted_enctype(krb5_context context, krb5_enctype etype)
+{
+    krb5_enctype *list, *ptr;
+    krb5_boolean ret;
+
+    if (krb5_get_permitted_enctypes(context, &list))
+       return(0);
+
+    
+    ret = 0;
+
+    for (ptr = list; *ptr; ptr++)
+       if (*ptr == etype)
+           ret = 1;
+
+    krb5_free_ktypes (context, list);
+
+    return(ret);
+}
+
+static krb5_error_code
+copy_ktypes(krb5_context ctx,
+           unsigned int nktypes,
+           krb5_enctype *oldktypes,
+           krb5_enctype **newktypes)
+{
+    unsigned int i;
+
+    *newktypes = NULL;
+    if (!nktypes)
+       return 0;
+
+    *newktypes = malloc(nktypes * sizeof(krb5_enctype));
+    if (*newktypes == NULL)
+       return ENOMEM;
+    for (i = 0; i < nktypes; i++)
+       (*newktypes)[i] = oldktypes[i];
+    return 0;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_copy_context(krb5_context ctx, krb5_context *nctx_out)
+{
+    krb5_error_code ret;
+    krb5_context nctx;
+
+    *nctx_out = NULL;
+    if (ctx == NULL)
+       return EINVAL;          /* XXX */
+
+    nctx = malloc(sizeof(*nctx));
+    if (nctx == NULL)
+       return ENOMEM;
+
+    *nctx = *ctx;
+
+    nctx->in_tkt_ktypes = NULL;
+    nctx->in_tkt_ktype_count = 0;
+    nctx->tgs_ktypes = NULL;
+    nctx->tgs_ktype_count = 0;
+    nctx->default_realm = NULL;
+    nctx->profile = NULL;
+    nctx->db_context = NULL;
+    nctx->ser_ctx_count = 0;
+    nctx->ser_ctx = NULL;
+    nctx->prompt_types = NULL;
+    nctx->os_context->default_ccname = NULL;
+
+    memset(&nctx->preauth_plugins, 0, sizeof(nctx->preauth_plugins));
+    nctx->preauth_context = NULL;
+
+    memset(&nctx->libkrb5_plugins, 0, sizeof(nctx->libkrb5_plugins));
+    nctx->vtbl = NULL;
+    nctx->locate_fptrs = NULL;
+
+    memset(&nctx->err, 0, sizeof(nctx->err));
+
+    ret = copy_ktypes(nctx, ctx->in_tkt_ktype_count,
+                     ctx->in_tkt_ktypes, &nctx->in_tkt_ktypes);
+    if (ret)
+       goto errout;
+    nctx->in_tkt_ktype_count = ctx->in_tkt_ktype_count;
+
+    ret = copy_ktypes(nctx, ctx->tgs_ktype_count,
+                     ctx->tgs_ktypes, &nctx->in_tkt_ktypes);
+    if (ret)
+       goto errout;
+    nctx->tgs_ktype_count = ctx->tgs_ktype_count;
+
+    if (ctx->os_context->default_ccname != NULL) {
+       nctx->os_context->default_ccname =
+           strdup(ctx->os_context->default_ccname);
+       if (nctx->os_context->default_ccname == NULL) {
+           ret = ENOMEM;
+           goto errout;
+       }
+    }
+    ret = krb5_get_profile(ctx, &nctx->profile);
+    if (ret)
+       goto errout;
+
+errout:
+    if (ret) {
+       krb5_free_context(nctx);
+    } else {
+       *nctx_out = nctx;
+    }
+    return ret;
 }