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