1 /*
2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * lib/krb5/krb/get_in_tkt.c
6  *
7  * Copyright 1995 by the Massachusetts Institute of Technology.
8  * All Rights Reserved.
9  *
10  * Export of this software from the United States of America may
11  *   require a specific license from the United States Government.
12  *   It is the responsibility of any person or organization contemplating
13  *   export to obtain such a license before exporting.
14  *
15  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16  * distribute this software and its documentation for any purpose and
17  * without fee is hereby granted, provided that the above copyright
18  * notice appear in all copies and that both that copyright notice and
19  * this permission notice appear in supporting documentation, and that
20  * the name of M.I.T. not be used in advertising or publicity pertaining
21  * to distribution of the software without specific, written prior
22  * permission.  Furthermore if you modify this software you must label
23  * your software as modified software and not distribute it in such a
24  * fashion that it might be confused with the original M.I.T. software.
25  * M.I.T. makes no representations about the suitability of
26  * this software for any purpose.  It is provided "as is" without express
27  * or implied warranty.
28  */
29 
30 #include "k5-int.h"
31 #ifdef HAVE_MEMORY_H
32 #include <memory.h>
33 #endif
34 #include <locale.h>
35 
36 /* helper function: convert flags to necessary KDC options */
37 #define flags2options(flags) (flags & KDC_TKT_COMMON_MASK)
38 
39 /* Get a TGT for use at the remote host */
40 krb5_error_code KRB5_CALLCONV
41 krb5_fwd_tgt_creds(krb5_context context, krb5_auth_context auth_context, char *rhost, krb5_principal client, krb5_principal server, krb5_ccache cc, int forwardable, krb5_data *outbuf)
42 
43 
44 
45 
46 
47 
48                           /* Should forwarded TGT also be forwardable? */
49 
50 {
51     krb5_replay_data replaydata;
52     krb5_data * scratch = 0;
53     krb5_address **addrs = NULL;
54     krb5_error_code retval;
55     krb5_creds creds, tgt;
56     krb5_creds *pcreds;
57     krb5_flags kdcoptions;
58     int close_cc = 0;
59     int free_rhost = 0;
60     krb5_enctype enctype = 0;
61     krb5_keyblock *session_key;
62     krb5_boolean old_use_conf_ktypes = context->use_conf_ktypes;
63 
64     memset((char *)&creds, 0, sizeof(creds));
65     memset((char *)&tgt, 0, sizeof(creds));
66 
67     if (cc == 0) {
68       if ((retval = krb5int_cc_default(context, &cc)))
69 	goto errout;
70       close_cc = 1;
71     }
72     retval = krb5_auth_con_getkey (context, auth_context, &session_key);
73     if (retval)
74       goto errout;
75     if (session_key) {
76 	enctype = session_key->enctype;
77 	krb5_free_keyblock (context, session_key);
78 	session_key = NULL;
79     } else if (server) { /* must server be non-NULL when rhost is given? */
80 	/* Try getting credentials to see what the remote side supports.
81 	   Not bulletproof, just a heuristic.  */
82 	krb5_creds in, *out = 0;
83 	memset (&in, 0, sizeof(in));
84 
85 	retval = krb5_copy_principal (context, server, &in.server);
86 	if (retval)
87 	    goto punt;
88 	retval = krb5_copy_principal (context, client, &in.client);
89 	if (retval)
90 	    goto punt;
91 	retval = krb5_get_credentials (context, 0, cc, &in, &out);
92 	if (retval)
93 	    goto punt;
94 	/* Got the credentials.  Okay, now record the enctype and
95 	   throw them away.  */
96 	enctype = out->keyblock.enctype;
97 	krb5_free_creds (context, out);
98     punt:
99 	krb5_free_cred_contents (context, &in);
100     }
101 
102     if ((retval = krb5_copy_principal(context, client, &creds.client)))
103 	goto errout;
104 
105     if ((retval = krb5_build_principal_ext(context, &creds.server,
106 					   client->realm.length,
107 					   client->realm.data,
108 					   KRB5_TGS_NAME_SIZE,
109 					   KRB5_TGS_NAME,
110 					   client->realm.length,
111 					   client->realm.data,
112 					   0)))
113 	goto errout;
114 
115     /* fetch tgt directly from cache */
116     context->use_conf_ktypes = 1;
117     retval = krb5_cc_retrieve_cred (context, cc, KRB5_TC_SUPPORTED_KTYPES,
118 				    &creds, &tgt);
119     context->use_conf_ktypes = old_use_conf_ktypes;
120     if (retval)
121 	goto errout;
122 
123     /* tgt->client must be equal to creds.client */
124     if (!krb5_principal_compare(context, tgt.client, creds.client)) {
125         /* Solaris Kerberos */
126         char *r_name = NULL;
127 	char *t_name = NULL;
128 	krb5_error_code r_err, t_err;
129 	t_err = krb5_unparse_name(context, tgt.client, &t_name);
130 	r_err = krb5_unparse_name(context, creds.client, &r_name);
131 	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
132 			    dgettext(TEXT_DOMAIN,
133 				    "Requested principal and ticket don't match:  Requested principal is '%s' and TGT principal is '%s'"),
134 			    r_err ? "unknown" : r_name,
135 			    t_err ? "unknown" : t_name);
136 	if (r_name)
137 	    krb5_free_unparsed_name(context, r_name);
138 	if (t_name)
139 	    krb5_free_unparsed_name(context, t_name);
140 	retval = KRB5_PRINC_NOMATCH;
141 	goto errout;
142     }
143 
144     if (!tgt.ticket.length) {
145 	retval = KRB5_NO_TKT_SUPPLIED;
146 	goto errout;
147     }
148 
149     if (tgt.addresses && *tgt.addresses) {
150       if (rhost == NULL) {
151 	if (krb5_princ_type(context, server) != KRB5_NT_SRV_HST) {
152 retval = KRB5_FWD_BAD_PRINCIPAL;
153  goto errout;
154 	}
155 
156 	if (krb5_princ_size(context, server) < 2){
157 	  retval = KRB5_CC_BADNAME;
158 	  goto errout;
159 	}
160 
161 	rhost = malloc(server->data[1].length+1);
162 	if (!rhost) {
163 	  retval = ENOMEM;
164 	  goto errout;
165 	}
166 	free_rhost = 1;
167 	/* Solaris Kerberos */
168 	(void) memcpy(rhost, server->data[1].data, server->data[1].length);
169 	rhost[server->data[1].length] = '\0';
170       }
171 
172 	retval = krb5_os_hostaddr(context, rhost, &addrs);
173 	if (retval)
174 	    goto errout;
175     }
176 
177     creds.keyblock.enctype = enctype;
178     creds.times = tgt.times;
179     creds.times.starttime = 0;
180     kdcoptions = flags2options(tgt.ticket_flags)|KDC_OPT_FORWARDED;
181 
182     if (!forwardable) /* Reset KDC_OPT_FORWARDABLE */
183       kdcoptions &= ~(KDC_OPT_FORWARDABLE);
184 
185     if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions,
186 					addrs, &creds, &pcreds))) {
187 	if (enctype) {
188 	    creds.keyblock.enctype = 0;
189 	    if ((retval = krb5_get_cred_via_tkt(context, &tgt, kdcoptions,
190 						addrs, &creds, &pcreds)))
191 		goto errout;
192 	}
193 	else goto errout;
194     }
195     retval = krb5_mk_1cred(context, auth_context, pcreds,
196                            &scratch, &replaydata);
197     krb5_free_creds(context, pcreds);
198 
199     /*
200      * Solaris Kerberos: changed this logic from the MIT 1.2.1 version to be
201      * more robust.
202      */
203     if (scratch) {
204 	if (retval)
205 	    krb5_free_data(context, scratch);
206 	else {
207 	    *outbuf = *scratch;
208 	    krb5_xfree(scratch);
209 	}
210     }
211 
212 errout:
213     if (addrs)
214 	krb5_free_addresses(context, addrs);
215     /* Solaris Kerberos */
216     if (close_cc)
217 	(void) krb5_cc_close(context, cc);
218     if (free_rhost)
219 	free(rhost);
220     krb5_free_cred_contents(context, &creds);
221     krb5_free_cred_contents(context, &tgt);
222     return retval;
223 }
224