get_init_creds_opt extensibility
authorTom Yu <tlyu@mit.edu>
Tue, 30 Jan 2007 21:38:47 +0000 (21:38 +0000)
committerTom Yu <tlyu@mit.edu>
Tue, 30 Jan 2007 21:38:47 +0000 (21:38 +0000)
 r18922@cathode-dark-space:  coffman | 2006-12-04 18:30:15 -0500
 First cut at making the get_init_creds_opt structure extendable
 and adding library functions to set options for preauthentication
 plugins.

 This does *not* include a compatibility function to work like
 Heimdal's krb5_get_init_creds_opt_set_pkinit() function.

 Hopefully, the test code that doesn't belong in kinit.c is
 obvious.

 r18929@cathode-dark-space:  coffman | 2006-12-07 10:01:20 -0500
 Remove extra "user_id" parameter.

 Add function which duplicates the Heimdal interface (if we can agree on
 what the matching attribute names should be).

 r18934@cathode-dark-space:  coffman | 2006-12-08 15:28:03 -0500
 Update to use the simplified interface for krb5_get_init_creds_opt_set_pa()

 Add code in kinit to process "-X" options as preauth options and pass
 them along.

 r18936@cathode-dark-space:  coffman | 2006-12-11 12:04:26 -0500
 Move prototypes for get_init_creds_opt_get_pa() and
 krb5_get_init_creds_opt_free_pa() into the
 preauth_plugin.h header rather than krb5.hin.

ticket: new
status: open
component: krb5-libs

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19127 dc483132-0cff-0310-8789-dd5450dbe970

15 files changed:
src/clients/kinit/kinit.c
src/clients/kpasswd/kpasswd.c
src/clients/kpasswd/ksetpwd.c
src/include/k5-int.h
src/include/krb5/krb5.hin
src/include/krb5/preauth_plugin.h
src/lib/krb5/krb/get_in_tkt.c
src/lib/krb5/krb/gic_keytab.c
src/lib/krb5/krb/gic_opt.c
src/lib/krb5/krb/gic_pwd.c
src/lib/krb5/krb/preauth2.c
src/lib/krb5/libkrb5.exports
src/lib/krb5_32.def
src/plugins/preauth/cksum_body/cksum_body_main.c
src/plugins/preauth/wpse/wpse_main.c

index 452d98cf0babeb573bc19ca21e71b4479978eb78..f8e5b77a4e0229c3d19ba9fd60efa8a940d29cd4 100644 (file)
@@ -38,6 +38,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <time.h>
+#include <errno.h>
 #include <com_err.h>
 
 #ifdef GETOPT_LONG
@@ -143,6 +144,9 @@ struct k_opts
     char* k4_cache_name;
 
     action_type action;
+
+    int num_pa_opts;
+    krb5_gic_opt_pa_data *pa_opts;
 };
 
 struct k5_data
@@ -283,6 +287,37 @@ static void extended_com_err_fn (const char *myprog, errcode_t code,
     fprintf (stderr, "\n");
 }
 
+static int
+add_preauth_opt(struct k_opts *opts, char *av)
+{
+    char *sep, *v;
+    krb5_gic_opt_pa_data *p, *x;
+
+    if (opts->num_pa_opts == 0) {
+       opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data));
+       if (opts->pa_opts == NULL)
+           return ENOMEM;
+    } else {
+       size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data);
+       x = realloc(opts->pa_opts, newsize);
+       if (x == NULL)
+           return ENOMEM;
+       opts->pa_opts = x;
+    }
+    p = &opts->pa_opts[opts->num_pa_opts];
+    sep = strchr(av, '=');
+    if (sep) {
+       *sep = '\0';
+       v = ++sep;
+       p->value = v;
+    } else {
+       p->value = "yes";
+    }
+    p->attr = av;
+    opts->num_pa_opts++;
+    return 0;
+}
+
 static char *
 parse_options(argc, argv, opts, progname)
     int argc;
@@ -296,7 +331,7 @@ parse_options(argc, argv, opts, progname)
     int use_k5 = 0;
     int i;
 
-    while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:v"))
+    while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:vX:"))
           != -1) {
        switch (i) {
        case 'V':
@@ -380,6 +415,14 @@ parse_options(argc, argv, opts, progname)
                opts->k5_cache_name = optarg;
            }
            break;
+       case 'X':
+           code = add_preauth_opt(opts, optarg);
+           if (code)
+           {
+               com_err(progname, code, "while adding preauth option");
+               errflg++;
+           }
+           break;
 #if 0
            /*
              A little more work is needed before we can enable this
@@ -752,12 +795,15 @@ k5_kinit(opts, k5)
     krb5_keytab keytab = 0;
     krb5_creds my_creds;
     krb5_error_code code = 0;
-    krb5_get_init_creds_opt options;
+    krb5_get_init_creds_opt *options = NULL;
+    int i;
 
     if (!got_k5)
        return 0;
 
-    krb5_get_init_creds_opt_init(&options);
+    code = krb5_get_init_creds_opt_alloc(k5->ctx, &options);
+    if (code)
+       goto cleanup;
     memset(&my_creds, 0, sizeof(my_creds));
 
     /*
@@ -766,17 +812,17 @@ k5_kinit(opts, k5)
     */
 
     if (opts->lifetime)
-       krb5_get_init_creds_opt_set_tkt_life(&options, opts->lifetime);
+       krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime);
     if (opts->rlife)
-       krb5_get_init_creds_opt_set_renew_life(&options, opts->rlife);
+       krb5_get_init_creds_opt_set_renew_life(options, opts->rlife);
     if (opts->forwardable)
-       krb5_get_init_creds_opt_set_forwardable(&options, 1);
+       krb5_get_init_creds_opt_set_forwardable(options, 1);
     if (opts->not_forwardable)
-       krb5_get_init_creds_opt_set_forwardable(&options, 0);
+       krb5_get_init_creds_opt_set_forwardable(options, 0);
     if (opts->proxiable)
-       krb5_get_init_creds_opt_set_proxiable(&options, 1);
+       krb5_get_init_creds_opt_set_proxiable(options, 1);
     if (opts->not_proxiable)
-       krb5_get_init_creds_opt_set_proxiable(&options, 0);
+       krb5_get_init_creds_opt_set_proxiable(options, 0);
     if (opts->addresses)
     {
        krb5_address **addresses = NULL;
@@ -785,10 +831,10 @@ k5_kinit(opts, k5)
            com_err(progname, code, "getting local addresses");
            goto cleanup;
        }
-       krb5_get_init_creds_opt_set_address_list(&options, addresses);
+       krb5_get_init_creds_opt_set_address_list(options, addresses);
     }
     if (opts->no_addresses)
-       krb5_get_init_creds_opt_set_address_list(&options, NULL);
+       krb5_get_init_creds_opt_set_address_list(options, NULL);
 
     if ((opts->action == INIT_KT) && opts->keytab_name)
     {
@@ -800,20 +846,49 @@ k5_kinit(opts, k5)
        }
     }
 
+    for (i = 0; i < opts->num_pa_opts; i++) {
+       code = krb5_get_init_creds_opt_set_pa(k5->ctx, options,
+                                             opts->pa_opts[i].attr,
+                                             opts->pa_opts[i].value);
+       if (code != 0) {
+           com_err(progname, code, "while setting '%s'='%s'",
+                   opts->pa_opts[i].attr, opts->pa_opts[i].value);
+           goto cleanup;
+       }
+    }
+
+#if 0  /* XXX Testing... */
+    code = krb5_get_init_creds_opt_set_pkinit(
+           k5->ctx,                            /* context */
+           options,                            /* get_init_creds_opt */
+           NULL,                               /* principal */
+           "/tmp/x509up_u20010",               /* X509_user_identity */
+           "/etc/grid-security/certificates",  /* X509_anchors */
+           NULL,                               /* X509_chain_list */
+           NULL,                               /* X509_revoke_list */
+           0,                                  /* flags */
+           NULL,                               /* prompter_fct */
+           NULL,                               /* prompter_data */
+           NULL);                              /* password */
+    if (code) {
+       com_err(progname, code, "while setting pkinit options");
+       goto cleanup;
+    }
+#endif
     switch (opts->action) {
     case INIT_PW:
        code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me,
                                            0, kinit_prompter, 0,
                                            opts->starttime, 
                                            opts->service_name,
-                                           &options);
+                                           options);
        break;
     case INIT_KT:
        code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me,
                                          keytab,
                                          opts->starttime, 
                                          opts->service_name,
-                                         &options);
+                                         options);
        break;
     case VALIDATE:
        code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc,
@@ -876,9 +951,16 @@ k5_kinit(opts, k5)
     notix = 0;
 
  cleanup:
+    if (options)
+       krb5_get_init_creds_opt_free(k5->ctx, options);
     if (my_creds.client == k5->me) {
        my_creds.client = 0;
     }
+    if (opts->pa_opts) {
+       free(opts->pa_opts);
+       opts->pa_opts = NULL;
+       opts->num_pa_opts = 0;
+    }
     krb5_free_cred_contents(k5->ctx, &my_creds);
     if (keytab)
        krb5_kt_close(k5->ctx, keytab);
index 95e33ff6f0b31516963821e90b99d700a5c4157e..204a8bfdbdfc03f8eb84edf0905720dde0ade262 100644 (file)
@@ -49,7 +49,7 @@ int main(int argc, char *argv[])
    krb5_principal princ;
    char *pname;
    krb5_ccache ccache;
-   krb5_get_init_creds_opt opts;
+   krb5_get_init_creds_opt *opts = NULL;
    krb5_creds creds;
 
    char pw[1024];
@@ -102,26 +102,31 @@ int main(int argc, char *argv[])
        get_name_from_passwd_file(argv[0], context, &princ);
    }
 
-   krb5_get_init_creds_opt_init(&opts);
-   krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);
-   krb5_get_init_creds_opt_set_renew_life(&opts, 0);
-   krb5_get_init_creds_opt_set_forwardable(&opts, 0);
-   krb5_get_init_creds_opt_set_proxiable(&opts, 0);
+   if ((ret = krb5_get_init_creds_opt_alloc(context, &opts))) {
+        com_err(argv[0], ret, "allocating krb5_get_init_creds_opt");
+        exit(1);
+   }
+   krb5_get_init_creds_opt_set_tkt_life(opts, 5*60);
+   krb5_get_init_creds_opt_set_renew_life(opts, 0);
+   krb5_get_init_creds_opt_set_forwardable(opts, 0);
+   krb5_get_init_creds_opt_set_proxiable(opts, 0);
 
    if ((ret = krb5_get_init_creds_password(context, &creds, princ, NULL,
                                           krb5_prompter_posix, NULL, 
-                                          0, "kadmin/changepw", &opts))) {
+                                          0, "kadmin/changepw", opts))) {
       if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
         com_err(argv[0], 0,
                 "Password incorrect while getting initial ticket");
       else
         com_err(argv[0], ret, "getting initial ticket");
+      krb5_get_init_creds_opt_free(context, opts);
       exit(1);
    }
 
    pwlen = sizeof(pw);
    if ((ret = krb5_read_password(context, P1, P2, pw, &pwlen))) {
       com_err(argv[0], ret, "while reading password");
+      krb5_get_init_creds_opt_free(context, opts);
       exit(1);
    }
 
@@ -129,6 +134,7 @@ int main(int argc, char *argv[])
                                   &result_code, &result_code_string,
                                   &result_string))) {
       com_err(argv[0], ret, "changing password");
+      krb5_get_init_creds_opt_free(context, opts);
       exit(1);
    }
 
@@ -138,6 +144,7 @@ int main(int argc, char *argv[])
             result_string.length?": ":"",
             (int) result_string.length,
             result_string.data ? result_string.data : "");
+      krb5_get_init_creds_opt_free(context, opts);
       exit(2);
    }
 
@@ -145,6 +152,7 @@ int main(int argc, char *argv[])
        free(result_string.data);
    if (result_code_string.data != NULL)
        free(result_code_string.data);
+   krb5_get_init_creds_opt_free(context, opts);
 
    printf("Password changed.\n");
    exit(0);
index 148e6865165a00e4618716a93125265e70d4d2d5..2eec397944315e244a356210db8f126fd4ac5a2b 100644 (file)
@@ -34,8 +34,6 @@ static void get_init_creds_opt_init( krb5_get_init_creds_opt *outOptions )
 {
     krb5_preauthtype    preauth[] = { KRB5_PADATA_ENC_TIMESTAMP };
     krb5_enctype        etypes[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_CRC};
-    memset( outOptions, 0, sizeof(*outOptions) );
-    krb5_get_init_creds_opt_init(outOptions);
     krb5_get_init_creds_opt_set_address_list(outOptions, NULL);
     krb5_get_init_creds_opt_set_etype_list( outOptions, etypes, sizeof(etypes)/sizeof(krb5_enctype) );
     krb5_get_init_creds_opt_set_preauth_list(outOptions, preauth, sizeof(preauth)/sizeof(krb5_preauthtype) );
@@ -128,17 +126,21 @@ static kbrccache_t userinitcontext(
                }
                if( kres != 0 || have_credentials == 0 )
                {
-                       krb5_get_init_creds_opt options;
-                       get_init_creds_opt_init(&options);
+                       krb5_get_init_creds_opt *options = NULL;
+                       kres = krb5_get_init_creds_opt_alloc(kcontext, &options);
+                       if ( kres == 0 )
+                       {
+                               get_init_creds_opt_init(options);
 /*
 ** no valid credentials - get new ones
 */
-                       kres = krb5_get_init_creds_password( kcontext, &kcreds, kme, pPass,
-                                       NULL /*prompter*/, 
-                                       NULL /*data*/,
-                                       0 /*starttime*/,
-                                       0 /*in_tkt_service*/,
-                                       &options /*options*/ );
+                               kres = krb5_get_init_creds_password( kcontext, &kcreds, kme, pPass,
+                                               NULL /*prompter*/, 
+                                               NULL /*data*/,
+                                               0 /*starttime*/,
+                                               0 /*in_tkt_service*/,
+                                               options /*options*/ );
+                       }
                        if( kres == 0 )
                        {
                                if( numCreds <= 0 )
@@ -148,6 +150,7 @@ static kbrccache_t userinitcontext(
                                if( kres == 0 )
                                        have_credentials = 1;
                        }
+                       krb5_get_init_creds_opt_free(kcontext, options);
                }
 #ifdef NOTUSED
                if( have_credentials )
index ac4bb62d26a4230e7e4e39a3fc58705f972b9722..21b0f4d01bec0c9351a300ce78bac96060c7e2ae 100644 (file)
@@ -876,6 +876,7 @@ typedef struct _krb5_preauth_context {
        krb5_error_code (*client_process)(krb5_context context,
                                          void *plugin_context,
                                          void *request_context,
+                                         krb5_get_init_creds_opt *opt,
                                          preauth_get_client_data_proc get_data_proc,
                                          krb5_preauth_client_rock *rock,
                                          krb5_kdc_req *request,
@@ -893,6 +894,7 @@ typedef struct _krb5_preauth_context {
        krb5_error_code (*client_tryagain)(krb5_context context,
                                           void *plugin_context,
                                           void *request_context,
+                                          krb5_get_init_creds_opt *opt,
                                           preauth_get_client_data_proc get_data_proc,
                                           krb5_preauth_client_rock *rock,
                                           krb5_kdc_req *request,
@@ -908,6 +910,7 @@ typedef struct _krb5_preauth_context {
                                           krb5_data *s2kparams,
                                           krb5_keyblock *as_key,
                                           krb5_pa_data **new_pa_data);
+       supply_gic_opts_proc client_supply_gic_opts;
        void (*client_req_init)(krb5_context context, void *plugin_context,
                               void **request_context);
        void (*client_req_fini)(krb5_context context, void *plugin_context,
@@ -1014,6 +1017,74 @@ void krb5_free_etype_info
 /*
  * End "preauth.h"
  */
+
+/*
+ * Extending the krb5_get_init_creds_opt structure.  The original
+ * krb5_get_init_creds_opt structure is defined publicly.  The
+ * new extended version is private.  The original interface
+ * assumed a pre-allocated structure which was passed to
+ * krb5_get_init_creds_init().  The new interface assumes that
+ * the caller will call krb5_get_init_creds_alloc() and
+ * krb5_get_init_creds_free().
+ *
+ * Callers MUST NOT call krb5_get_init_creds_init() after allocating an
+ * opts structure using krb5_get_init_creds_alloc().  To do so will
+ * introduce memory leaks.  Unfortunately, there is no way to enforce
+ * this behavior.
+ *
+ * Two private flags are added for backward compatibility.
+ * KRB5_GET_INIT_CREDS_OPT_EXTENDED says that the structure was allocated
+ * with the new krb5_get_init_creds_opt_alloc() function.
+ * KRB5_GET_INIT_CREDS_OPT_SHADOWED is set to indicate that the extended
+ * structure is a shadow copy of an original krb5_get_init_creds_opt
+ * structure.  
+ * If KRB5_GET_INIT_CREDS_OPT_SHADOWED is set after a call to
+ * krb5int_gic_opt_to_opte(), the resulting extended structure should be
+ * freed (using krb5_get_init_creds_free).  Otherwise, the original
+ * structure was already extended and there is no need to free it.
+ */
+
+#define KRB5_GET_INIT_CREDS_OPT_EXTENDED 0x80000000
+#define KRB5_GET_INIT_CREDS_OPT_SHADOWED 0x40000000
+
+#define krb5_gic_opt_is_extended(s) \
+    (((s)->flags & KRB5_GET_INIT_CREDS_OPT_EXTENDED) ? 1 : 0)
+#define krb5_gic_opt_is_shadowed(s) \
+    (((s)->flags & KRB5_GET_INIT_CREDS_OPT_SHADOWED) ? 1 : 0)
+
+
+typedef struct _krb5_gic_opt_private {
+    int num_preauth_data;
+    krb5_gic_opt_pa_data *preauth_data;
+} krb5_gic_opt_private;
+
+typedef struct _krb5_gic_opt_ext {
+    krb5_flags flags;
+    krb5_deltat tkt_life;
+    krb5_deltat renew_life;
+    int forwardable;
+    int proxiable;
+    krb5_enctype *etype_list;
+    int etype_list_length;
+    krb5_address **address_list;
+    krb5_preauthtype *preauth_list;
+    int preauth_list_length;
+    krb5_data *salt;
+    /*
+     * Do not change anything above this point in this structure.
+     * It is identical to the public krb5_get_init_creds_opt structure.
+     * New members must be added below.
+     */
+    krb5_gic_opt_private *opt_private;
+} krb5_gic_opt_ext;
+
+krb5_error_code
+krb5int_gic_opt_to_opte(krb5_context context,
+                        krb5_get_init_creds_opt *opt,
+                        krb5_gic_opt_ext **opte,
+                        unsigned int force,
+                        const char *where);
+
 krb5_error_code
 krb5int_copy_data_contents (krb5_context, const krb5_data *, krb5_data *);
 
@@ -1040,14 +1111,14 @@ krb5_get_init_creds
                void *prompter_data,
                krb5_deltat start_time,
                char *in_tkt_service,
-               krb5_get_init_creds_opt *gic_options,
+               krb5_gic_opt_ext *gic_options,
                krb5_gic_get_as_key_fct gak,
                void *gak_data,
                int *master,
                krb5_kdc_rep **as_reply);
 
-void krb5int_populate_gic_opt (
-    krb5_context, krb5_get_init_creds_opt *,
+krb5_error_code krb5int_populate_gic_opt (
+    krb5_context, krb5_gic_opt_ext **,
     krb5_flags options, krb5_address * const *addrs, krb5_enctype *ktypes,
     krb5_preauthtype *pre_auth_types, krb5_creds *creds);
 
@@ -1062,7 +1133,8 @@ krb5_error_code KRB5_CALLCONV krb5_do_preauth
         krb5_enctype *etype, krb5_keyblock *as_key,
         krb5_prompter_fct prompter, void *prompter_data,
         krb5_gic_get_as_key_fct gak_fct, void *gak_data,
-        krb5_preauth_client_rock *get_data_rock);
+        krb5_preauth_client_rock *get_data_rock,
+        krb5_gic_opt_ext *opte);
 krb5_error_code KRB5_CALLCONV krb5_do_preauth_tryagain
        (krb5_context context,
         krb5_kdc_req *request,
@@ -1074,7 +1146,8 @@ krb5_error_code KRB5_CALLCONV krb5_do_preauth_tryagain
         krb5_enctype *etype, krb5_keyblock *as_key,
         krb5_prompter_fct prompter, void *prompter_data,
         krb5_gic_get_as_key_fct gak_fct, void *gak_data,
-        krb5_preauth_client_rock *get_data_rock);
+        krb5_preauth_client_rock *get_data_rock,
+        krb5_gic_opt_ext *opte);
 void KRB5_CALLCONV krb5_init_preauth_context
        (krb5_context);
 void KRB5_CALLCONV krb5_free_preauth_context
@@ -1082,7 +1155,7 @@ void KRB5_CALLCONV krb5_free_preauth_context
 void KRB5_CALLCONV krb5_clear_preauth_context_use_counts
        (krb5_context);
 void KRB5_CALLCONV krb5_preauth_prepare_request
-       (krb5_context, krb5_get_init_creds_opt *, krb5_kdc_req *);
+       (krb5_context, krb5_gic_opt_ext *, krb5_kdc_req *);
 void KRB5_CALLCONV krb5_preauth_request_context_init
        (krb5_context);
 void KRB5_CALLCONV krb5_preauth_request_context_fini
index 1c5f1573804a7fdfe9e327e6ef045d1e12477720..ff0a31be7912b6e64e9941a246a572f5c0e035ec 100644 (file)
@@ -2081,6 +2081,16 @@ typedef struct _krb5_get_init_creds_opt {
 #define KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT  0x0100
 
 
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_alloc
+(krb5_context context,
+               krb5_get_init_creds_opt **opt);
+
+void KRB5_CALLCONV
+krb5_get_init_creds_opt_free
+(krb5_context context,
+               krb5_get_init_creds_opt *opt);
+
 void KRB5_CALLCONV
 krb5_get_init_creds_opt_init
 (krb5_get_init_creds_opt *opt);
@@ -2132,6 +2142,27 @@ krb5_get_init_creds_opt_set_change_password_prompt
 (krb5_get_init_creds_opt *opt,
                int prompt);
 
+/* Generic preauth option attribute/value pairs */
+typedef struct _krb5_gic_opt_pa_data {
+    char *attr;
+    char *value;
+} krb5_gic_opt_pa_data;
+
+/*
+ * This function allows the caller to supply options to preauth
+ * plugins.  Preauth plugin modules are given a chance to look
+ * at each option at the time this function is called in ordre
+ * to check the validity of the option.
+ * The 'opt' pointer supplied to this function must have been
+ * obtained using krb5_get_init_creds_opt_alloc()
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_pa
+               (krb5_context context,
+               krb5_get_init_creds_opt *opt,
+               const char *attr,
+               const char *value);
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds_password
 (krb5_context context,
index f8a9db1a175d9298b65b2970c3a6f945dd267158..7243a00b173dc6284767b0f37e444d84eb139176 100644 (file)
@@ -157,6 +157,17 @@ typedef krb5_error_code
                           krb5_keyblock *as_key,
                           void *gak_data);
 
+/*
+ * Client function which receives krb5_get_init_creds_opt information.
+ * The attr and value information supplied should be copied locally by
+ * the module if it wishes to reference it after returning from this call.
+ */
+typedef krb5_error_code
+(*supply_gic_opts_proc)(krb5_context context,
+                       void *plugin_context,
+                       krb5_get_init_creds_opt *opt,
+                       const char *attr,
+                       const char *value);
 /*
  * The function table / structure which a preauth client module must export as
  * "preauthentication_client_0".  If the interfaces work correctly, future
@@ -207,6 +218,7 @@ typedef struct krb5plugin_preauth_client_ftable_v0 {
     krb5_error_code (*process)(krb5_context context,
                               void *plugin_context,
                               void *request_context,
+                              krb5_get_init_creds_opt *opt,
                               preauth_get_client_data_proc get_data_proc,
                               struct _krb5_preauth_client_rock *rock,
                               krb5_kdc_req *request,
@@ -227,8 +239,9 @@ typedef struct krb5plugin_preauth_client_ftable_v0 {
     krb5_error_code (*tryagain)(krb5_context context,
                                void *plugin_context,
                                void *request_context,
+                               krb5_get_init_creds_opt *opt,
                                preauth_get_client_data_proc get_data_proc,
-                               struct _krb5_preauth_client_rock *rock,
+                               struct _krb5_preauth_client_rock *rock,
                                krb5_kdc_req *request,
                                krb5_data *encoded_request_body,
                                krb5_data *encoded_previous_request,
@@ -241,6 +254,12 @@ typedef struct krb5plugin_preauth_client_ftable_v0 {
                                krb5_data *salt, krb5_data *s2kparams,
                                krb5_keyblock *as_key,
                                krb5_pa_data **out_pa_data);
+    /*
+     * Client function which receives krb5_get_init_creds_opt information.
+     * The attr and value information supplied should be copied locally by
+     * the module if it wishes to reference it after returning from this call.
+     */
+    supply_gic_opts_proc gic_opts;
 } krb5plugin_preauth_client_ftable_v0;
 
 /*
@@ -323,4 +342,31 @@ typedef struct krb5plugin_preauth_server_ftable_v0 {
                                              void *pa_module_context,
                                              void **request_pa_context);
 } krb5plugin_preauth_server_ftable_v0;
+
+
+/*
+ * This function allows a preauth plugin to obtain preauth
+ * options.  The preauth_data returned from this function
+ * should be freed by calling krb5_get_init_creds_opt_free_pa().
+ *
+ * The 'opt' pointer supplied to this function must have been
+ * obtained using krb5_get_init_creds_opt_alloc()
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_get_pa
+               (krb5_context context,
+               krb5_get_init_creds_opt *opt,
+               int *num_preauth_data,
+               krb5_gic_opt_pa_data **preauth_data);
+
+/*
+ * This function frees the preauth_data that was returned by
+ * krb5_get_init_creds_opt_get_pa().
+ */
+void KRB5_CALLCONV
+krb5_get_init_creds_opt_free_pa
+               (krb5_context context,
+                int num_preauth_data,
+                krb5_gic_opt_pa_data *preauth_data);
+
 #endif /* KRB5_PREAUTH_PLUGIN_H_INCLUDED */
index 12476d3fab1a4b6bae7ba7fe5a6ae67673317cbc..efde9147b6aa02261df14b7bc4a3d210b26a60f3 100644 (file)
@@ -843,7 +843,7 @@ krb5_get_init_creds(krb5_context context,
                    void *prompter_data,
                    krb5_deltat start_time,
                    char *in_tkt_service,
-                   krb5_get_init_creds_opt *options,
+                   krb5_gic_opt_ext *options,
                    krb5_gic_get_as_key_fct gak_fct,
                    void *gak_data,
                    int  *use_master,
@@ -1111,30 +1111,21 @@ krb5_get_init_creds(krb5_context context,
                                       &salt, &s2kparams, &etype, &as_key,
                                       prompter, prompter_data,
                                       gak_fct, gak_data,
-                                      &get_data_rock)))
+                                      &get_data_rock, options)))
                goto cleanup;
        } else {
-           if (preauth_to_use != NULL) {
-               /*
-                * Retry after an error other than PREAUTH_NEEDED,
-                * using e-data to figure out what to change.
-                */
-               ret = krb5_do_preauth_tryagain(context,
-                                              &request,
-                                              encoded_request_body,
-                                              encoded_previous_request,
-                                              preauth_to_use, &request.padata,
-                                              err_reply,
-                                              &salt, &s2kparams, &etype,
-                                              &as_key,
-                                              prompter, prompter_data,
-                                              gak_fct, gak_data,
-                                              &get_data_rock);
-           } else {
-               /* No preauth supplied, so can't query the plug-ins. */
-               ret = KRB5KRB_ERR_GENERIC;
-           }
-           if (ret) {
+           /* retrying after an error other than PREAUTH_NEEDED, using e-data
+            * to figure out what to change */
+           if (krb5_do_preauth_tryagain(context,
+                                        &request,
+                                        encoded_request_body,
+                                        encoded_previous_request,
+                                        preauth_to_use, &request.padata,
+                                        err_reply,
+                                        &salt, &s2kparams, &etype, &as_key,
+                                        prompter, prompter_data,
+                                        gak_fct, gak_data,
+                                        &get_data_rock, options)) {
                /* couldn't come up with anything better */
                ret = err_reply->error + ERROR_TABLE_BASE_krb5;
            }
@@ -1214,7 +1205,7 @@ krb5_get_init_creds(krb5_context context,
                               local_as_reply->padata, &kdc_padata,
                               &salt, &s2kparams, &etype, &as_key, prompter,
                               prompter_data, gak_fct, gak_data,
-                              &get_data_rock)))
+                              &get_data_rock, options)))
        goto cleanup;
 
     /* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
index ba704e6be8e29f7e1c4ca8ee3007a8a129a7df56..cae04955aee5825838f4e5463ceb666d29560b79 100644 (file)
@@ -76,11 +76,18 @@ krb5_get_as_key_keytab(
 }
 
 krb5_error_code KRB5_CALLCONV
-krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_keytab arg_keytab, krb5_deltat start_time, char *in_tkt_service, krb5_get_init_creds_opt *options)
+krb5_get_init_creds_keytab(krb5_context context,
+                          krb5_creds *creds,
+                          krb5_principal client,
+                          krb5_keytab arg_keytab,
+                          krb5_deltat start_time,
+                          char *in_tkt_service,
+                          krb5_get_init_creds_opt *options)
 {
    krb5_error_code ret, ret2;
    int use_master;
    krb5_keytab keytab;
+   krb5_gic_opt_ext *opte = NULL;
 
    if (arg_keytab == NULL) {
        if ((ret = krb5_kt_default(context, &keytab)))
@@ -89,12 +96,17 @@ krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_princip
        keytab = arg_keytab;
    }
 
+   ret = krb5int_gic_opt_to_opte(context, options, &opte, 1,
+                                "krb5_get_init_creds_keytab");
+   if (ret)
+      return ret;
+
    use_master = 0;
 
    /* first try: get the requested tkt from any kdc */
 
    ret = krb5_get_init_creds(context, creds, client, NULL, NULL,
-                            start_time, in_tkt_service, options,
+                            start_time, in_tkt_service, opte,
                             krb5_get_as_key_keytab, (void *) keytab,
                             &use_master,NULL);
 
@@ -115,7 +127,7 @@ krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_princip
       use_master = 1;
 
       ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL,
-                                start_time, in_tkt_service, options,
+                                start_time, in_tkt_service, opte,
                                 krb5_get_as_key_keytab, (void *) keytab,
                                 &use_master, NULL);
       
@@ -139,6 +151,8 @@ krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_princip
       do any prompting or changing for keytabs, that's it. */
 
 cleanup:
+   if (opte && krb5_gic_opt_is_shadowed(opte))
+       krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
    if (arg_keytab == NULL)
        krb5_kt_close(context, keytab);
 
@@ -152,15 +166,18 @@ krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options,
                              krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
 {
     krb5_error_code retval;
-    krb5_get_init_creds_opt opt;
+    krb5_gic_opt_ext *opte;
     char * server = NULL;
     krb5_keytab keytab;
     krb5_principal client_princ, server_princ;
     int use_master = 0;
     
-    krb5int_populate_gic_opt(context, &opt,
-                            options, addrs, ktypes,
-                            pre_auth_types, creds);
+    retval = krb5int_populate_gic_opt(context, &opte,
+                                     options, addrs, ktypes,
+                                     pre_auth_types, creds);
+    if (retval)
+       return retval;
+
     if (arg_keytab == NULL) {
        retval = krb5_kt_default(context, &keytab);
        if (retval)
@@ -176,10 +193,11 @@ krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options,
     retval = krb5_get_init_creds (context,
                                  creds, creds->client,  
                                  krb5_prompter_posix,  NULL,
-                                 0, server, &opt,
+                                 0, server, opte,
                                  krb5_get_as_key_keytab, (void *)keytab,
                                  &use_master, ret_as_reply);
     krb5_free_unparsed_name( context, server);
+    krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
     if (retval) {
        goto cleanup;
     }
index 3ec59e8aab160972fe89f5e36cf3445eca756b88..10fe759a6a6ec2e66bc4a7a273638d39005673c0 100644 (file)
@@ -64,11 +64,419 @@ krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, krb5_data *salt)
    opt->salt = salt;
 }
 
+/*
+ * Extending the krb5_get_init_creds_opt structure.  The original
+ * krb5_get_init_creds_opt structure is defined publicly.  The
+ * new extended version is private.  The original interface
+ * assumed a pre-allocated structure which was passed to
+ * krb5_get_init_creds_init().  The new interface assumes that
+ * the caller will call krb5_get_init_creds_alloc() and
+ * krb5_get_init_creds_free().
+ *
+ * Callers MUST NOT call krb5_get_init_creds_init() after allocating an
+ * opts structure using krb5_get_init_creds_alloc().  To do so will
+ * introduce memory leaks.  Unfortunately, there is no way to enforce
+ * this behavior.
+ *
+ * Two private flags are added for backward compatibility.
+ * KRB5_GET_INIT_CREDS_OPT_EXTENDED says that the structure was allocated
+ * with the new krb5_get_init_creds_opt_alloc() function.
+ * KRB5_GET_INIT_CREDS_OPT_SHADOWED is set to indicate that the extended
+ * structure is a shadow copy of an original krb5_get_init_creds_opt
+ * structure.  
+ * If KRB5_GET_INIT_CREDS_OPT_SHADOWED is set after a call to
+ * krb5int_gic_opt_to_opte(), the resulting extended structure should be
+ * freed (using krb5_get_init_creds_free).  Otherwise, the original
+ * structure was already extended and there is no need to free it.
+ */
+
+/* Forward prototype */
+static void
+free_gic_opt_ext_preauth_data(krb5_context context,
+                             krb5_gic_opt_ext *opte);
+
+static krb5_error_code
+krb5int_gic_opte_private_alloc(krb5_context context, krb5_gic_opt_ext *opte)
+{
+    if (NULL == opte || !krb5_gic_opt_is_extended(opte))
+       return EINVAL;
+
+    opte->opt_private = calloc(1, sizeof(*opte->opt_private));
+    if (NULL == opte->opt_private) {
+       return ENOMEM;
+    }
+    /* Allocate any private stuff */
+    opte->opt_private->num_preauth_data = 0;
+    opte->opt_private->preauth_data = NULL;
+    return 0;
+}
+
+static krb5_error_code
+krb5int_gic_opte_private_free(krb5_context context, krb5_gic_opt_ext *opte)
+{
+    if (NULL == opte || !krb5_gic_opt_is_extended(opte))
+       return EINVAL;
+       
+    /* Free up any private stuff */
+    if (opte->opt_private->preauth_data != NULL)
+       free_gic_opt_ext_preauth_data(context, opte);
+    free(opte->opt_private);
+    opte->opt_private = NULL;
+    return 0;
+}
+
+static krb5_gic_opt_ext *
+krb5int_gic_opte_alloc(krb5_context context)
+{
+    krb5_gic_opt_ext *opte;
+    krb5_error_code code;
+
+    opte = calloc(1, sizeof(*opte));
+    if (NULL == opte)
+       return NULL;
+    opte->flags = KRB5_GET_INIT_CREDS_OPT_EXTENDED;
+
+    code = krb5int_gic_opte_private_alloc(context, opte);
+    if (code) {
+       krb5int_set_error(&context->err, code,
+               "krb5int_gic_opte_alloc: krb5int_gic_opte_private_alloc failed");
+       free(opte);
+       return NULL;
+    }
+    return(opte);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_alloc(krb5_context context,
+                             krb5_get_init_creds_opt **opt)
+{
+    krb5_gic_opt_ext *opte;
+
+    if (NULL == opt)
+       return EINVAL;
+    *opt = NULL;
+
+    /*
+     * We return a new extended structure cast as a krb5_get_init_creds_opt
+     */
+    opte = krb5int_gic_opte_alloc(context);
+    if (NULL == opte)
+       return ENOMEM;
+
+    *opt = (krb5_get_init_creds_opt *) opte;
+    return 0;
+}
+
 void KRB5_CALLCONV
-krb5_get_init_creds_opt_set_change_password_prompt(krb5_get_init_creds_opt *opt, int prompt)
+krb5_get_init_creds_opt_free(krb5_context context,
+                            krb5_get_init_creds_opt *opt)
 {
-   if (prompt)
-     opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT;
-   else
-     opt->flags &= ~KRB5_GET_INIT_CREDS_OPT_CHG_PWD_PRMPT;
+    krb5_gic_opt_ext *opte;
+
+    if (NULL == opt)
+       return;
+
+    /* Don't touch it if we didn't allocate it */
+    if (!krb5_gic_opt_is_extended(opt))
+       return;
+    
+    opte = (krb5_gic_opt_ext *)opt;
+    if (opte->opt_private)
+       krb5int_gic_opte_private_free(context, opte);
+
+    free(opte);
+}
+
+static krb5_error_code
+krb5int_gic_opte_copy(krb5_context context,
+                     krb5_get_init_creds_opt *opt,
+                     krb5_gic_opt_ext **opte)
+{
+    krb5_gic_opt_ext *oe;
+
+    oe = krb5int_gic_opte_alloc(context);
+    if (NULL == oe)
+       return ENOMEM;
+    memcpy(oe, opt, sizeof(*opt));
+    /* Fix these -- overwritten by the copy */
+    oe->flags |= ( KRB5_GET_INIT_CREDS_OPT_EXTENDED |
+                  KRB5_GET_INIT_CREDS_OPT_SHADOWED);
+
+    *opte = oe;
+    return 0;
+}
+
+/*
+ * Convert a krb5_get_init_creds_opt pointer to a pointer to
+ * an extended, krb5_gic_opt_ext pointer.  If the original
+ * pointer already points to an extended structure, then simply
+ * return the original pointer.  Otherwise, if 'force' is non-zero,
+ * allocate an extended structure and copy the original over it.
+ * If the original pointer did not point to an extended structure
+ * and 'force' is zero, then return an error.  This is used in
+ * cases where the original *should* be an extended structure.
+ */
+krb5_error_code
+krb5int_gic_opt_to_opte(krb5_context context,
+                       krb5_get_init_creds_opt *opt,
+                       krb5_gic_opt_ext **opte,
+                       unsigned int force,
+                       const char *where)
+{
+    if (!krb5_gic_opt_is_extended(opt)) {
+       if (force) {
+           return krb5int_gic_opte_copy(context, opt, opte);
+       } else {
+           krb5int_set_error(&context->err, EINVAL,
+                   "%s: attempt to convert non-extended krb5_get_init_creds_opt",
+                   where);
+           return EINVAL;
+       }
+    }
+    /* If it is already extended, just return it */
+    *opte = (krb5_gic_opt_ext *)opt;
+    return 0;
+}
+
+static void
+free_gic_opt_ext_preauth_data(krb5_context context,
+                             krb5_gic_opt_ext *opte)
+{
+    int i;
+
+    if (NULL == opte || !krb5_gic_opt_is_extended(opte))
+       return;
+    if (NULL == opte->opt_private || NULL == opte->opt_private->preauth_data)
+       return;
+
+    for (i = 0; i < opte->opt_private->num_preauth_data; i++) {
+       if (opte->opt_private->preauth_data[i].attr != NULL)
+           free(opte->opt_private->preauth_data[i].attr);
+       if (opte->opt_private->preauth_data[i].value != NULL)
+           free(opte->opt_private->preauth_data[i].value);
+    }
+    free(opte->opt_private->preauth_data);
+    opte->opt_private->preauth_data = NULL;
+    opte->opt_private->num_preauth_data = 0;
+}
+
+static krb5_error_code
+add_gic_opt_ext_preauth_data(krb5_context context,
+                            krb5_gic_opt_ext *opte,
+                            const char *attr,
+                            const char *value)
+{
+    size_t newsize;
+    int i;
+    krb5_gic_opt_pa_data *newpad;
+
+    newsize = opte->opt_private->num_preauth_data + 1;
+    newsize = newsize * sizeof(*opte->opt_private->preauth_data);
+    if (opte->opt_private->preauth_data == NULL)
+       newpad = malloc(newsize);
+    else
+       newpad = realloc(opte->opt_private->preauth_data, newsize);
+    if (newpad == NULL)
+       return ENOMEM;
+
+    i = opte->opt_private->num_preauth_data;
+    newpad[i].attr = strdup(attr);
+    if (newpad[i].attr == NULL)
+       return ENOMEM;
+    newpad[i].value = strdup(value);
+    if (newpad[i].value == NULL) {
+       free(newpad[i].attr);
+       return ENOMEM;
+    }
+    opte->opt_private->num_preauth_data += 1;
+    opte->opt_private->preauth_data = newpad;
+    return 0;
+}
+
+/*
+ * This function allows the caller to supply options to preauth
+ * plugins.  Preauth plugin modules are given a chance to look
+ * at each option at the time this function is called in ordre
+ * to check the validity of the option.
+ * The 'opt' pointer supplied to this function must have been
+ * obtained using krb5_get_init_creds_opt_alloc()
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_pa(krb5_context context,
+                              krb5_get_init_creds_opt *opt,
+                              const char *attr,
+                              const char *value)
+{
+    krb5_error_code retval;
+    krb5_gic_opt_ext *opte;
+
+    retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+                                    "krb5_get_init_creds_opt_set_pa");
+    if (retval)
+       return retval;
+
+    /*
+     * Copy the option into the extended get_init_creds_opt structure
+     */
+    retval = add_gic_opt_ext_preauth_data(context, opte, attr, value);
+    if (retval)
+       return retval;
+
+    /*
+     * Give the plugins a chance to look at the option now.
+     */
+    retval = krb5_preauth_supply_preauth_data(context, opte, attr, value);
+    return retval;
+}
+
+/*
+ * This function allows a preauth plugin to obtain preauth
+ * options.  The preauth_data returned from this function
+ * should be freed by calling krb5_get_init_creds_opt_free_pa().
+ *
+ * The 'opt' pointer supplied to this function must have been
+ * obtained using krb5_get_init_creds_opt_alloc()
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_get_pa(krb5_context context,
+                              krb5_get_init_creds_opt *opt,
+                              int *num_preauth_data,
+                              krb5_gic_opt_pa_data **preauth_data)
+{
+    krb5_error_code retval;
+    krb5_gic_opt_ext *opte;
+    krb5_gic_opt_pa_data *p = NULL;
+    int i;
+    size_t allocsize;
+
+    retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+                                    "krb5_get_init_creds_opt_get_pa");
+    if (retval)
+       return retval;
+
+    if (num_preauth_data == NULL || preauth_data == NULL)
+       return EINVAL;
+
+    *num_preauth_data = 0;
+    *preauth_data = NULL;
+
+    if (opte->opt_private->num_preauth_data == 0)
+       return 0;
+
+    allocsize =
+           opte->opt_private->num_preauth_data * sizeof(krb5_gic_opt_pa_data);
+    p = malloc(allocsize);
+    if (p == NULL)
+       return ENOMEM;
+
+    /* Init these to make cleanup easier */
+    for (i = 0; i < opte->opt_private->num_preauth_data; i++) {
+       p[i].attr = NULL;
+       p[i].value = NULL;
+    }
+
+    for (i = 0; i < opte->opt_private->num_preauth_data; i++) {
+       p[i].attr = strdup(opte->opt_private->preauth_data[i].attr);
+       p[i].value = strdup(opte->opt_private->preauth_data[i].value);
+       if (p[i].attr == NULL || p[i].value == NULL)
+           goto cleanup;
+    }
+    *num_preauth_data = i;
+    *preauth_data = p;
+    return 0;
+cleanup:
+    for (i = 0; i < opte->opt_private->num_preauth_data; i++) {
+       if (p[i].attr != NULL)
+           free(p[i].attr);
+       if (p[i].value != NULL)
+           free(p[i].value);
+    }
+    free(p);
+    return ENOMEM;
+}
+
+/*
+ * This function frees the preauth_data that was returned by 
+ * krb5_get_init_creds_opt_get_pa().
+ */
+void KRB5_CALLCONV
+krb5_get_init_creds_opt_free_pa(krb5_context context,
+                               int num_preauth_data,
+                               krb5_gic_opt_pa_data *preauth_data)
+{
+    int i;
+
+    if (num_preauth_data <= 0 || preauth_data == NULL)
+       return;
+
+    for (i = 0; i < num_preauth_data; i++) {
+       if (preauth_data[i].attr != NULL)
+           free(preauth_data[i].attr);
+       if (preauth_data[i].value != NULL)
+           free(preauth_data[i].value);
+    }
+    free(preauth_data);
+}
+
+
+/*
+ * This function is provided for compatibility with Heimdal's
+ * function of the same name.  We ignore the principal,
+ * password, and prompter parameters.
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_pkinit(krb5_context context,
+                                  krb5_get_init_creds_opt *opt,
+                                  krb5_principal principal,
+                                  const char *x509_user_identity,
+                                  const char *x509_anchors,
+                                  char * const * x509_chain_list,
+                                  char * const * x509_revoke_list,
+                                  int flags,
+                                  krb5_prompter_fct prompter,
+                                  void *prompter_data,
+                                  char *password)
+{
+    int i;
+    krb5_error_code retval;
+
+#define PKINIT_RSA_PROTOCOL 0x00000002 /* XXX */
+
+    if (x509_user_identity != NULL) {
+       retval = krb5_get_init_creds_opt_set_pa(context, opt,
+                           "X509_user_identity", x509_user_identity);
+       if (retval)
+           return retval;
+    }
+    if (x509_anchors != NULL) {
+       retval = krb5_get_init_creds_opt_set_pa(context, opt,
+                           "X509_anchors", x509_anchors);
+       if (retval)
+           return retval;
+    }
+    if (x509_chain_list != NULL) {
+       for (i = 0; x509_chain_list[i] != NULL; i++) {
+           retval = krb5_get_init_creds_opt_set_pa(context, opt,
+                           "X509_chain_list", x509_chain_list[i]);
+           if (retval)
+               return retval;
+       }
+    }
+    if (x509_revoke_list != NULL) {
+       for (i = 0; x509_revoke_list[i] != NULL; i++) {
+           retval = krb5_get_init_creds_opt_set_pa(context, opt,
+                           "X509_revoke_list", x509_revoke_list[i]);
+           if (retval)
+               return retval;
+       }
+    }
+    if (flags != 0) {
+       if (flags & PKINIT_RSA_PROTOCOL) {
+           retval = krb5_get_init_creds_opt_set_pa(context, opt,
+                           "flag_RSA_PROTOCOL", "yes");
+           if (retval)
+               return retval;
+       }
+    }
+    return retval;
 }
index f7f62f47a97aa7cdd9ab22eb5928ec8e394dd1a9..02d344c5df020944339feb419a4686de43d69184 100644 (file)
@@ -85,18 +85,28 @@ krb5_get_as_key_password(
 }
 
 krb5_error_code KRB5_CALLCONV
-krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_principal client, char *password, krb5_prompter_fct prompter, void *data, krb5_deltat start_time, char *in_tkt_service, krb5_get_init_creds_opt *options)
+krb5_get_init_creds_password(krb5_context context,
+                            krb5_creds *creds,
+                            krb5_principal client,
+                            char *password,
+                            krb5_prompter_fct prompter,
+                            void *data,
+                            krb5_deltat start_time,
+                            char *in_tkt_service,
+                            krb5_get_init_creds_opt *options)
 {
    krb5_error_code ret, ret2;
    int use_master;
    krb5_kdc_rep *as_reply;
    int tries;
    krb5_creds chpw_creds;
-   krb5_get_init_creds_opt chpw_opts;
+   krb5_get_init_creds_opt *chpw_opts = NULL;
    krb5_data pw0, pw1;
    char banner[1024], pw0array[1024], pw1array[1024];
    krb5_prompt prompt[2];
    krb5_prompt_type prompt_types[sizeof(prompt)/sizeof(prompt[0])];
+   krb5_gic_opt_ext *opte = NULL;
+   krb5_gic_opt_ext *chpw_opte = NULL;
 
    use_master = 0;
    as_reply = NULL;
@@ -119,10 +129,15 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ
    pw1.data[0] = '\0';
    pw1.length = sizeof(pw1array);
 
+   ret = krb5int_gic_opt_to_opte(context, options, &opte, 1,
+                                "krb5_get_init_creds_password");
+   if (ret)
+      goto cleanup;
+
    /* first try: get the requested tkt from any kdc */
 
    ret = krb5_get_init_creds(context, creds, client, prompter, data,
-                            start_time, in_tkt_service, options,
+                            start_time, in_tkt_service, opte,
                             krb5_get_as_key_password, (void *) &pw0,
                             &use_master, &as_reply);
 
@@ -151,7 +166,7 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ
          as_reply = NULL;
       }
       ret2 = krb5_get_init_creds(context, creds, client, prompter, data,
-                                start_time, in_tkt_service, options,
+                                start_time, in_tkt_service, opte,
                                 krb5_get_as_key_password, (void *) &pw0,
                                 &use_master, &as_reply);
       
@@ -197,15 +212,21 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ
 
    /* use a minimal set of options */
 
-   krb5_get_init_creds_opt_init(&chpw_opts);
-   krb5_get_init_creds_opt_set_tkt_life(&chpw_opts, 5*60);
-   krb5_get_init_creds_opt_set_renew_life(&chpw_opts, 0);
-   krb5_get_init_creds_opt_set_forwardable(&chpw_opts, 0);
-   krb5_get_init_creds_opt_set_proxiable(&chpw_opts, 0);
+   ret = krb5_get_init_creds_opt_alloc(context, &chpw_opts);
+   if (ret)
+      goto cleanup;
+   krb5_get_init_creds_opt_set_tkt_life(chpw_opts, 5*60);
+   krb5_get_init_creds_opt_set_renew_life(chpw_opts, 0);
+   krb5_get_init_creds_opt_set_forwardable(chpw_opts, 0);
+   krb5_get_init_creds_opt_set_proxiable(chpw_opts, 0);
+   ret = krb5int_gic_opt_to_opte(context, chpw_opts, &chpw_opte, 0,
+                       "krb5_get_init_creds_password (changing password)");
+   if (ret)
+      goto cleanup;
 
    if ((ret = krb5_get_init_creds(context, &chpw_creds, client,
                                  prompter, data,
-                                 start_time, "kadmin/changepw", &chpw_opts,
+                                 start_time, "kadmin/changepw", chpw_opte,
                                  krb5_get_as_key_password, (void *) &pw0,
                                  &use_master, NULL)))
       goto cleanup;
@@ -293,7 +314,7 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ
       is final.  */
 
    ret = krb5_get_init_creds(context, creds, client, prompter, data,
-                            start_time, in_tkt_service, options,
+                            start_time, in_tkt_service, opte,
                             krb5_get_as_key_password, (void *) &pw0,
                             &use_master, &as_reply);
 
@@ -373,6 +394,10 @@ cleanup:
       }
    }
 
+   if (chpw_opts)
+      krb5_get_init_creds_opt_free(context, chpw_opts);
+   if (opte && krb5_gic_opt_is_shadowed(opte))
+      krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
    memset(pw0array, 0, sizeof(pw0array));
    memset(pw1array, 0, sizeof(pw1array));
    krb5_free_cred_contents(context, &chpw_creds);
@@ -381,15 +406,20 @@ cleanup:
 
    return(ret);
 }
-void krb5int_populate_gic_opt (
-    krb5_context context, krb5_get_init_creds_opt *opt,
+krb5_error_code krb5int_populate_gic_opt (
+    krb5_context context, krb5_gic_opt_ext **opte,
     krb5_flags options, krb5_address * const *addrs, krb5_enctype *ktypes,
     krb5_preauthtype *pre_auth_types, krb5_creds *creds)
 {
   int i;
   krb5_int32 starttime;
+  krb5_get_init_creds_opt *opt;
+  krb5_error_code retval;
+
+    retval = krb5_get_init_creds_opt_alloc(context, &opt);
+    if (retval)
+       return(retval);
 
-    krb5_get_init_creds_opt_init(opt);
     if (addrs)
       krb5_get_init_creds_opt_set_address_list(opt, (krb5_address **) addrs);
     if (ktypes) {
@@ -413,6 +443,8 @@ void krb5int_populate_gic_opt (
         if (creds->times.starttime) starttime = creds->times.starttime;
         krb5_get_init_creds_opt_set_tkt_life(opt, creds->times.endtime - starttime);
     }
+    return krb5int_gic_opt_to_opte(context, opt, opte, 0,
+                                  "krb5int_populate_gic_opt");
 }
 
 /*
@@ -445,10 +477,10 @@ krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options,
     krb5_error_code retval;
     krb5_data pw0;
     char pw0array[1024];
-    krb5_get_init_creds_opt opt;
     char * server;
     krb5_principal server_princ, client_princ;
     int use_master = 0;
+    krb5_gic_opt_ext *opte = NULL;
 
     pw0array[0] = '\0';
     pw0.data = pw0array;
@@ -462,21 +494,26 @@ krb5_get_in_tkt_with_password(krb5_context context, krb5_flags options,
     } else {
        pw0.length = sizeof(pw0array);
     }
-    krb5int_populate_gic_opt(context, &opt,
-                            options, addrs, ktypes,
-                            pre_auth_types, creds);
-    retval = krb5_unparse_name( context, creds->server, &server);
+    retval = krb5int_populate_gic_opt(context, &opte,
+                                     options, addrs, ktypes,
+                                     pre_auth_types, creds);
     if (retval)
       return (retval);
+    retval = krb5_unparse_name( context, creds->server, &server);
+    if (retval) {
+      return (retval);
+      krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
+    }
     server_princ = creds->server;
     client_princ = creds->client;
         retval = krb5_get_init_creds (context,
                                           creds, creds->client,  
                                           krb5_prompter_posix,  NULL,
-                                          0, server, &opt,
+                                          0, server, opte,
                                      krb5_get_as_key_password, &pw0,
                                      &use_master, ret_as_reply);
          krb5_free_unparsed_name( context, server);
+         krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
        if (retval) {
          return (retval);
        }
index 598709a0c7fdd2cd9bbc6ce0fe2d8001211d2fdc..2a2756f33b3baed0e0e040eef98c9cccf52be8da 100644 (file)
@@ -163,6 +163,10 @@ krb5_init_preauth_context(krb5_context kcontext)
                context->modules[k].use_count = 0;
                context->modules[k].client_process = table->process;
                context->modules[k].client_tryagain = table->tryagain;
+               if (j == 0)
+                   context->modules[k].client_supply_gic_opts = table->gic_opts;
+               else
+                   context->modules[k].client_supply_gic_opts = NULL;
                context->modules[k].request_context = NULL;
                /*
                 * Only call request_init and request_fini once per plugin.
@@ -211,6 +215,52 @@ krb5_clear_preauth_context_use_counts(krb5_context context)
     }
 }
 
+/*
+ * Give all the preauth plugins a look at the preauth option which
+ * has just been set
+ */
+krb5_error_code
+krb5_preauth_supply_preauth_data(krb5_context context,
+                                krb5_gic_opt_ext *opte,
+                                const char *attr,
+                                const char *value)
+{
+    krb5_error_code retval;
+    int i;
+    void *pctx;
+    const char *emsg = NULL;
+
+    if (context->preauth_context == NULL)
+       krb5_init_preauth_context(context);
+    if (context->preauth_context == NULL) {
+       retval = EINVAL;
+       krb5int_set_error(&context->err, retval,
+               "krb5_preauth_supply_preauth_data: "
+               "Unable to initialize preauth context");
+       return retval;
+    }
+
+    /*
+     * Go down the list of preauth modules, and supply them with the
+     * attribute/value pair.
+     */
+    for (i = 0; i < context->preauth_context->n_modules; i++) {
+       if (context->preauth_context->modules[i].client_supply_gic_opts == NULL)
+           continue;
+       pctx = context->preauth_context->modules[i].plugin_context;
+       retval = (*context->preauth_context->modules[i].client_supply_gic_opts)
+                               (context, pctx,
+                                (krb5_get_init_creds_opt *)opte, attr, value); 
+       if (retval) {
+           emsg = krb5_get_error_message(context, retval);
+           krb5int_set_error(&context->err, retval, "Preauth plugin %s: %s",
+                             context->preauth_context->modules[i].name, emsg);
+           break;
+       }
+    }
+    return retval;
+}
+
 /* Free the per-krb5_context preauth_context. This means clearing any
  * plugin-specific context which may have been created, and then
  * freeing the context itself. */
@@ -405,7 +455,7 @@ client_data_proc(krb5_context kcontext,
  * involved things. */
 void KRB5_CALLCONV
 krb5_preauth_prepare_request(krb5_context kcontext,
-                            krb5_get_init_creds_opt *options,
+                            krb5_gic_opt_ext *opte,
                             krb5_kdc_req *request)
 {
     int i, j;
@@ -415,7 +465,7 @@ krb5_preauth_prepare_request(krb5_context kcontext,
     }
     /* Add the module-specific enctype list to the request, but only if
      * it's something we can safely modify. */
-    if (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
+    if (!(opte && (opte->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
        for (i = 0; i < kcontext->preauth_context->n_modules; i++) {
            if (kcontext->preauth_context->modules[i].enctypes == NULL)
                continue;
@@ -448,7 +498,8 @@ krb5_run_preauth_plugins(krb5_context kcontext,
                         krb5_pa_data ***out_pa_list,
                         int *out_pa_list_size,
                         int *module_ret,
-                        int *module_flags)
+                        int *module_flags,
+                        krb5_gic_opt_ext *opte)
 {
     int i;
     krb5_pa_data *out_pa_data;
@@ -487,6 +538,7 @@ krb5_run_preauth_plugins(krb5_context kcontext,
        ret = module->client_process(kcontext,
                                     module->plugin_context,
                                     *module->request_context_pp,
+                                    (krb5_get_init_creds_opt *)opte,
                                     client_data_proc,
                                     get_data_rock,
                                     request,
@@ -1299,7 +1351,8 @@ krb5_do_preauth_tryagain(krb5_context kcontext,
                         krb5_keyblock *as_key,
                         krb5_prompter_fct prompter, void *prompter_data,
                         krb5_gic_get_as_key_fct gak_fct, void *gak_data,
-                        krb5_preauth_client_rock *get_data_rock)
+                        krb5_preauth_client_rock *get_data_rock,
+                        krb5_gic_opt_ext *opte)
 {
     krb5_error_code ret;
     krb5_pa_data *out_padata;
@@ -1330,6 +1383,7 @@ krb5_do_preauth_tryagain(krb5_context kcontext,
            if ((*module->client_tryagain)(kcontext,
                                           module->plugin_context,
                                           *module->request_context_pp,
+                                          (krb5_get_init_creds_opt *)opte,
                                           client_data_proc,
                                           get_data_rock,
                                           request,
@@ -1362,7 +1416,8 @@ krb5_do_preauth(krb5_context context,
                krb5_keyblock *as_key,
                krb5_prompter_fct prompter, void *prompter_data,
                krb5_gic_get_as_key_fct gak_fct, void *gak_data,
-               krb5_preauth_client_rock *get_data_rock)
+               krb5_preauth_client_rock *get_data_rock,
+               krb5_gic_opt_ext *opte)
 {
     int h, i, j, out_pa_list_size;
     int seen_etype_info2 = 0;
@@ -1555,7 +1610,8 @@ krb5_do_preauth(krb5_context context,
                                                   &out_pa_list,
                                                   &out_pa_list_size,
                                                   &module_ret,
-                                                  &module_flags);
+                                                  &module_flags,
+                                                  opte);
                    if (ret == 0) {
                        if (module_ret == 0) {
                            if (paorder[h] == PA_REAL) {
index bca1e1e2b5fa59e2faa5fcdfaefbfa592282bf41..a46ad70dd1cfdb826d1e75823638f11a434610f8 100644 (file)
@@ -439,11 +439,16 @@ krb5_get_in_tkt_with_password
 krb5_get_in_tkt_with_skey
 krb5_get_init_creds
 krb5_get_init_creds_keytab
+krb5_get_init_creds_opt_alloc
+krb5_get_init_creds_opt_free
+krb5_get_init_creds_opt_free_pa
+krb5_get_init_creds_opt_get_pa
 krb5_get_init_creds_opt_init
 krb5_get_init_creds_opt_set_address_list
 krb5_get_init_creds_opt_set_change_password_prompt
 krb5_get_init_creds_opt_set_etype_list
 krb5_get_init_creds_opt_set_forwardable
+krb5_get_init_creds_opt_set_pa
 krb5_get_init_creds_opt_set_preauth_list
 krb5_get_init_creds_opt_set_proxiable
 krb5_get_init_creds_opt_set_renew_life
@@ -739,3 +744,10 @@ profile_update_file_data
 profile_update_relation
 profile_verify_node
 profile_write_tree_file
+krb5_set_error_message
+krb5_vset_error_message
+krb5_get_error_message
+krb5_free_error_message
+krb5_clear_error_message
+krb5int_init_context_kdc
+krb5_get_init_creds_opt_set_pkinit
index 078b8cae9984e3a8700fd2bb0c1811937f8e683a..5b833f45ef9ba58933fdcea606e7b0ae3f1ca2a9 100644 (file)
@@ -155,7 +155,12 @@ krb5_c_string_to_key_with_params
        krb5_get_in_tkt_with_password           ; DEPRECATED
        krb5_get_in_tkt_with_skey               ; DEPRECATED
        krb5_get_init_creds_keytab
+       krb5_get_init_creds_opt_alloc
+       krb5_get_init_creds_opt_free
+       krb5_get_init_creds_opt_free_pa
+       krb5_get_init_creds_opt_get_pa
        krb5_get_init_creds_opt_init
+       krb5_get_init_creds_opt_set_pa
        krb5_get_init_creds_opt_set_address_list
        krb5_get_init_creds_opt_set_etype_list
        krb5_get_init_creds_opt_set_forwardable
index 6b46b00a33ff5d82afc0f1aa98016e53993cfe35..cd19daf7ab739b2ac949e18abc46071820943781 100644 (file)
@@ -78,6 +78,7 @@ static krb5_error_code
 client_process(krb5_context kcontext,
               void *client_plugin_context,
               void *client_request_context,
+              krb5_get_init_creds_opt *opt,
               preauth_get_client_data_proc client_get_data_proc,
               struct _krb5_preauth_client_rock *rock,
               krb5_kdc_req *request,
@@ -99,6 +100,27 @@ client_process(krb5_context kcontext,
     krb5_error_code status = 0;
     krb5_int32 cksumtype, *enctypes;
     unsigned int i, n_enctypes, cksumtype_count;
+    int num_gic_info = 0;
+    krb5_gic_opt_pa_data *gic_info;
+
+    status = krb5_get_init_creds_opt_get_pa(kcontext, opt,
+                                           &num_gic_info, &gic_info);
+    if (status && status != ENOENT) {
+#ifdef DEBUG
+       fprintf(stderr, "Error from krb5_get_init_creds_opt_get_pa: %s\n",
+               error_message(status));
+#endif
+       return status;
+    }
+#ifdef DEBUG
+    fprintf(stderr, "(cksum_body) Got the following gic options:\n");
+#endif
+    for (i = 0; i < num_gic_info; i++) {
+#ifdef DEBUG
+       fprintf(stderr, "  '%s' = '%s'\n", gic_info[i].attr, gic_info[i].value);
+#endif
+    }
+    krb5_get_init_creds_opt_free_pa(kcontext, num_gic_info, gic_info);
 
     memset(&checksum, 0, sizeof(checksum));
 
@@ -193,6 +215,20 @@ client_process(krb5_context kcontext,
     return 0;
 }
 
+static krb5_error_code
+client_gic_opt(krb5_context kcontext,
+              void *plugin_context,
+              krb5_get_init_creds_opt *opt,
+              const char *attr,
+              const char *value)
+{
+#ifdef DEBUG
+    fprintf(stderr, "(cksum_body) client_gic_opt: received '%s' = '%s'\n",
+           attr, value); 
+#endif
+    return 0;
+}
+
 /* Initialize and tear down the server-side module, and do stat tracking. */
 static krb5_error_code
 server_init(krb5_context kcontext, void **module_context)
@@ -200,7 +236,7 @@ server_init(krb5_context kcontext, void **module_context)
     struct server_stats *stats;
     stats = malloc(sizeof(struct server_stats));
     if (stats == NULL)
-        return ENOMEM;
+       return ENOMEM;
     stats->successes = 0;
     stats->failures = 0;
     *module_context = stats;
@@ -506,6 +542,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
     NULL,                                  /* request fini function */
     client_process,                        /* process function */
     NULL,                                  /* try_again function */
+    client_gic_opt                         /* get init creds opt function */
 };
 
 struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {
index 46ea66230786e0c282e16ddee28421095641ce46..f85806387553f5a7470198446bb2dc78d416bf60 100644 (file)
@@ -90,6 +90,7 @@ static krb5_error_code
 client_process(krb5_context kcontext,
               void *plugin_context,
               void *request_context,
+              krb5_get_init_creds_opt *opt,
               preauth_get_client_data_proc client_get_data_proc,
               struct _krb5_preauth_client_rock *rock,
               krb5_kdc_req *request,
@@ -208,6 +209,21 @@ client_req_cleanup(krb5_context kcontext, void *plugin_context, void *req_contex
     return;
 }
 
+static krb5_error_code
+client_gic_opt(krb5_context kcontext,
+              void *plugin_context,
+              krb5_get_init_creds_opt *opt,
+              const char *attr,
+              const char *value)
+{
+#ifdef DEBUG
+    fprintf(stderr, "(wpse) client_gic_opt: received '%s' = '%s'\n",
+           attr, value);
+#endif
+    return 0;
+}
+
+
 /* Free state. */
 static krb5_error_code
 server_free_pa_request_context(krb5_context kcontext, void *plugin_context,
@@ -378,6 +394,7 @@ struct krb5plugin_preauth_client_ftable_v0 preauthentication_client_0 = {
     client_req_cleanup,                            /* request fini function */
     client_process,                        /* process function */
     NULL,                                  /* try_again function */
+    client_gic_opt                         /* get init creds opts function */
 };
 
 struct krb5plugin_preauth_server_ftable_v0 preauthentication_server_0 = {