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