xref: /illumos-gate/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gic_keytab.c (revision 257873cfc1dd3337766407f80397db60a56f2f5a)
1 /*
2  * Copyright 2008 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 #include "k5-int.h"
34 
35 /*ARGSUSED*/
36 static krb5_error_code
37 krb5_get_as_key_keytab(
38     krb5_context context,
39     krb5_principal client,
40     krb5_enctype etype,
41     krb5_prompter_fct prompter,
42     void *prompter_data,
43     krb5_data *salt,
44     krb5_data *params,
45     krb5_keyblock *as_key,
46     void *gak_data)
47 {
48     krb5_keytab keytab = (krb5_keytab) gak_data;
49     krb5_error_code ret;
50     krb5_keytab_entry kt_ent;
51     krb5_keyblock *kt_key;
52 
53     /* if there's already a key of the correct etype, we're done.
54        if the etype is wrong, free the existing key, and make
55        a new one. */
56 
57     if (as_key->length) {
58 	if (as_key->enctype == etype)
59 	    return(0);
60 
61 	krb5_free_keyblock_contents(context, as_key);
62 	as_key->length = 0;
63     }
64 
65     if (!krb5_c_valid_enctype(etype))
66 	return(KRB5_PROG_ETYPE_NOSUPP);
67 
68     /* Solaris Kerberos */
69     if ((ret = krb5_kt_get_entry(context, keytab, client,
70 				 0, /* don't have vno available */
71 				 etype, &kt_ent)) != NULL)
72 	return(ret);
73 
74     ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key);
75 
76     /* again, krb5's memory management is lame... */
77 
78     *as_key = *kt_key;
79     krb5_xfree(kt_key);
80 
81     (void) krb5_kt_free_entry(context, &kt_ent);
82 
83     return(ret);
84 }
85 
86 krb5_error_code KRB5_CALLCONV
87 krb5_get_init_creds_keytab(krb5_context context,
88 			   krb5_creds *creds,
89 			   krb5_principal client,
90 			   krb5_keytab arg_keytab,
91 			   krb5_deltat start_time,
92 			   char *in_tkt_service,
93 			   krb5_get_init_creds_opt *options)
94 {
95    krb5_error_code ret, ret2;
96    int use_master;
97    krb5_keytab keytab;
98    krb5_gic_opt_ext *opte = NULL;
99 
100    if (arg_keytab == NULL) {
101        if ((ret = krb5_kt_default(context, &keytab)))
102 	    return ret;
103    } else {
104        keytab = arg_keytab;
105    }
106 
107    ret = krb5int_gic_opt_to_opte(context, options, &opte, 1,
108 				 "krb5_get_init_creds_keytab");
109    if (ret)
110       return ret;
111 
112    use_master = 0;
113 
114    /* first try: get the requested tkt from any kdc */
115 
116    ret = krb5_get_init_creds(context, creds, client, NULL, NULL,
117 			     start_time, in_tkt_service, opte,
118 			     krb5_get_as_key_keytab, (void *) keytab,
119 			     &use_master,NULL);
120 
121    /* check for success */
122 
123    if (ret == 0)
124       goto cleanup;
125 
126    /* If all the kdc's are unavailable fail */
127 
128    if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE))
129       goto cleanup;
130 
131    /* if the reply did not come from the master kdc, try again with
132       the master kdc */
133 
134    if (!use_master) {
135       use_master = 1;
136 
137       ret2 = krb5_get_init_creds(context, creds, client, NULL, NULL,
138 				 start_time, in_tkt_service, opte,
139 				 krb5_get_as_key_keytab, (void *) keytab,
140 				 &use_master, NULL);
141 
142       if (ret2 == 0) {
143 	 ret = 0;
144 	 goto cleanup;
145       }
146 
147       /* if the master is unreachable, return the error from the
148 	 slave we were able to contact */
149 
150       if ((ret2 == KRB5_KDC_UNREACH) ||
151 	  (ret2 == KRB5_REALM_CANT_RESOLVE) ||
152 	  (ret2 == KRB5_REALM_UNKNOWN))
153 	 goto cleanup;
154 
155       ret = ret2;
156    }
157 
158    /* at this point, we have a response from the master.  Since we don't
159       do any prompting or changing for keytabs, that's it. */
160 
161 cleanup:
162    if (opte && krb5_gic_opt_is_shadowed(opte))
163        krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
164    if (arg_keytab == NULL)
165        (void) krb5_kt_close(context, keytab); /* Solaris Kerberos */
166 
167    return(ret);
168 }
169 krb5_error_code KRB5_CALLCONV
170 krb5_get_in_tkt_with_keytab(krb5_context context, krb5_flags options,
171 			      krb5_address *const *addrs, krb5_enctype *ktypes,
172 			      krb5_preauthtype *pre_auth_types,
173 			      krb5_keytab arg_keytab, krb5_ccache ccache,
174 			      krb5_creds *creds, krb5_kdc_rep **ret_as_reply)
175 {
176     krb5_error_code retval;
177     krb5_gic_opt_ext *opte;
178     char * server = NULL;
179     krb5_keytab keytab;
180     krb5_principal client_princ, server_princ;
181     int use_master = 0;
182 
183     retval = krb5int_populate_gic_opt(context, &opte,
184 				      options, addrs, ktypes,
185 				      pre_auth_types, creds);
186     if (retval)
187 	return retval;
188 
189     if (arg_keytab == NULL) {
190 	retval = krb5_kt_default(context, &keytab);
191 	if (retval)
192 	    return retval;
193     }
194     else keytab = arg_keytab;
195 
196     retval = krb5_unparse_name( context, creds->server, &server);
197     if (retval)
198 	goto cleanup;
199     server_princ = creds->server;
200     client_princ = creds->client;
201     retval = krb5_get_init_creds (context,
202 				  creds, creds->client,
203 				  krb5_prompter_posix,  NULL,
204 				  0, server, opte,
205 				  krb5_get_as_key_keytab, (void *)keytab,
206 				  &use_master, ret_as_reply);
207     krb5_free_unparsed_name( context, server);
208     krb5_get_init_creds_opt_free(context, (krb5_get_init_creds_opt *)opte);
209     if (retval) {
210 	goto cleanup;
211     }
212 	if (creds->server)
213 	    krb5_free_principal( context, creds->server);
214 	if (creds->client)
215 	    krb5_free_principal( context, creds->client);
216 	creds->client = client_princ;
217 	creds->server = server_princ;
218 
219     /* store it in the ccache! */
220     if (ccache)
221 	if ((retval = krb5_cc_store_cred(context, ccache, creds)))
222 	    goto cleanup;
223  cleanup:    if (arg_keytab == NULL)
224      krb5_kt_close(context, keytab);
225     return retval;
226 }
227 
228