923579487211358e89807d3587b11103ac3021d0
[krb5.git] / src / lib / krb5 / krb / gic_keytab.c
1 /*
2  * lib/krb5/krb/gic_keytab.c
3  *
4  * Copyright (C) 2002, 2003, 2008 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  * 
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 #ifndef LEAN_CLIENT 
27
28 #include "k5-int.h"
29
30 static krb5_error_code
31 krb5_get_as_key_keytab(
32     krb5_context context,
33     krb5_principal client,
34     krb5_enctype etype,
35     krb5_prompter_fct prompter,
36     void *prompter_data,
37     krb5_data *salt,
38     krb5_data *params,
39     krb5_keyblock *as_key,
40     void *gak_data)
41 {
42     krb5_keytab keytab = (krb5_keytab) gak_data;
43     krb5_error_code ret;
44     krb5_keytab_entry kt_ent;
45     krb5_keyblock *kt_key;
46
47     /* if there's already a key of the correct etype, we're done.
48        if the etype is wrong, free the existing key, and make
49        a new one. */
50
51     if (as_key->length) {
52         if (as_key->enctype == etype)
53             return(0);
54
55         krb5_free_keyblock_contents(context, as_key);
56         as_key->length = 0;
57     }
58
59     if (!krb5_c_valid_enctype(etype))
60         return(KRB5_PROG_ETYPE_NOSUPP);
61
62     if ((ret = krb5_kt_get_entry(context, keytab, client,
63                                  0, /* don't have vno available */
64                                  etype, &kt_ent)))
65         return(ret);
66
67     ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key);
68
69     /* again, krb5's memory management is lame... */
70
71     *as_key = *kt_key;
72     free(kt_key);
73
74     (void) krb5_kt_free_entry(context, &kt_ent);
75
76     return(ret);
77 }
78
79 krb5_error_code KRB5_CALLCONV
80 krb5_get_init_creds_keytab(krb5_context context,
81                            krb5_creds *creds,
82                            krb5_principal client,
83                            krb5_keytab arg_keytab,
84                            krb5_deltat start_time,
85                            char *in_tkt_service,
86                            krb5_get_init_creds_opt *options)
87 {
88    krb5_error_code ret, ret2;
89    int use_master;
90    krb5_keytab keytab;
91    krb5_gic_opt_ext *opte = NULL;
92
93    if (arg_keytab == NULL) {
94        if ((ret = krb5_kt_default(context, &keytab)))
95             return ret;
96    } else {
97        keytab = arg_keytab;
98    }
99
100    ret = krb5int_gic_opt_to_opte(context, options, &opte, 1,
101                                  "krb5_get_init_creds_keytab");
102    if (ret)
103       return ret;
104
105    use_master = 0;
106
107    /* first try: get the requested tkt from any kdc */
108
109    ret = krb5_get_init_creds(context, creds, client, NULL, NULL,
110                              start_time, in_tkt_service, opte,
111                              krb5_get_as_key_keytab, (void *) keytab,
112                              &use_master,NULL);
113
114    /* check for success */
115
116    if (ret == 0)
117       goto cleanup;
118
119    /* If all the kdc's are unavailable fail */
120
121    if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE))
122       goto cleanup;
123
124    /* if the reply did not come from the master kdc, try again with
125       the master kdc */
126
127    if (!use_master) {
128       use_master = 1;
129
130       ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL,
131                                  start_time, in_tkt_service, opte,
132                                  krb5_get_as_key_keytab, (void *) keytab,
133                                  &use_master, NULL);
134       
135       if (ret2 == 0) {
136          ret = 0;
137          goto cleanup;
138       }
139
140       /* if the master is unreachable, return the error from the
141          slave we were able to contact */
142
143       if ((ret2 == KRB5_KDC_UNREACH) ||
144           (ret2 == KRB5_REALM_CANT_RESOLVE) ||
145           (ret2 == KRB5_REALM_UNKNOWN))
146          goto cleanup;
147
148       ret = ret2;
149    }
150
151    /* at this point, we have a response from the master.  Since we don't
152       do any prompting or changing for keytabs, that's it. */
153
154 cleanup:
155    if (opte && krb5_gic_opt_is_shadowed(opte))
156        krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
157    if (arg_keytab == NULL)
158        krb5_kt_close(context, keytab);
159
160    return(ret);
161 }
162 krb5_error_code KRB5_CALLCONV
163 krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options,
164                               krb5_address *const *addrs, krb5_enctype *ktypes,
165                               krb5_preauthtype *pre_auth_types,
166                               krb5_keytab arg_keytab, krb5_ccache ccache,
167                               krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
168 {
169     krb5_error_code retval;
170     krb5_gic_opt_ext *opte;
171     char * server = NULL;
172     krb5_keytab keytab;
173     krb5_principal client_princ, server_princ;
174     int use_master = 0;
175     
176     retval = krb5int_populate_gic_opt(context, &opte,
177                                       options, addrs, ktypes,
178                                       pre_auth_types, creds);
179     if (retval)
180         return retval;
181
182     if (arg_keytab == NULL) {
183         retval = krb5_kt_default(context, &keytab);
184         if (retval)
185             return retval;
186     }
187     else keytab = arg_keytab;
188     
189     retval = krb5_unparse_name( context, creds->server, &server);
190     if (retval)
191         goto cleanup;
192     server_princ = creds->server;
193     client_princ = creds->client;
194     retval = krb5_get_init_creds (context,
195                                   creds, creds->client,  
196                                   krb5_prompter_posix,  NULL,
197                                   0, server, opte,
198                                   krb5_get_as_key_keytab, (void *)keytab,
199                                   &use_master, ret_as_reply);
200     krb5_free_unparsed_name( context, server);
201     krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
202     if (retval) {
203         goto cleanup;
204     }
205         if (creds->server)
206             krb5_free_principal( context, creds->server);
207         if (creds->client)
208             krb5_free_principal( context, creds->client);
209         creds->client = client_princ;
210         creds->server = server_princ;
211         
212     /* store it in the ccache! */
213     if (ccache)
214         if ((retval = krb5_cc_store_cred(context, ccache, creds)))
215             goto cleanup;
216  cleanup:    if (arg_keytab == NULL)
217      krb5_kt_close(context, keytab);
218     return retval;
219 }
220
221 #endif /* LEAN_CLIENT */
222