Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * 3. Neither the name of the Institute nor the names of its contributors
18 : * may be used to endorse or promote products derived from this software
19 : * without specific prior written permission.
20 : *
21 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 : * SUCH DAMAGE.
32 : */
33 :
34 : #include "krb5_locl.h"
35 :
36 : #define CHECKED_ALLOC(dst) do { \
37 : if ((ALLOC(dst, 1)) == NULL) { \
38 : ret = krb5_enomem(context); \
39 : goto out; \
40 : } \
41 : } while (0)
42 :
43 : #define CHECKED_COPY(cp_func, dst, src) do { \
44 : if (cp_func(src, dst)) { \
45 : ret = krb5_enomem(context); \
46 : goto out; \
47 : } \
48 : } while (0)
49 : #define CHECKED_COPY_PPC2KCI(cp_func, dst, src) \
50 : CHECKED_COPY(cp_func, krb_cred_info->dst, &ppcreds[i]->src)
51 :
52 : #define CHECKED_ALLOC_ASSIGN(dst, src) do { \
53 : if ((ALLOC(dst, 1)) == NULL) { \
54 : ret = krb5_enomem(context); \
55 : goto out; \
56 : } else \
57 : *dst = src; \
58 : } while (0)
59 : #define CHECKED_ALLOC_ASSIGN_PPC2KCI(dst, src) \
60 : CHECKED_ALLOC_ASSIGN(krb_cred_info->dst, ppcreds[i]->src)
61 :
62 : #define CHECKED_ALLOC_COPY(cp_func, dst, src) do { \
63 : if ((ALLOC(dst, 1)) == NULL || cp_func(src, dst)) { \
64 : ret = krb5_enomem(context); \
65 : goto out; \
66 : } \
67 : } while (0)
68 : #define CHECKED_ALLOC_COPY_PPC2KCI(cp_func, dst, src) \
69 : CHECKED_ALLOC_COPY(cp_func, krb_cred_info->dst, &ppcreds[i]->src)
70 :
71 : /**
72 : * Make a KRB-CRED PDU with N credentials.
73 : *
74 : * @param context A kerberos 5 context.
75 : * @param auth_context The auth context with the key to encrypt the out_data.
76 : * @param ppcreds A null-terminated array of credentials to forward.
77 : * @param ppdata The output KRB-CRED (to be freed by caller).
78 : * @param replay_data (unused).
79 : *
80 : * @return Return an error code or 0.
81 : *
82 : * @ingroup krb5_credential
83 : */
84 :
85 : /* ARGSUSED */
86 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
87 0 : krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
88 : krb5_creds **ppcreds, krb5_data **ppdata,
89 : krb5_replay_data *replay_data)
90 : {
91 0 : krb5_error_code ret;
92 0 : krb5_data out_data;
93 :
94 0 : ret = _krb5_mk_ncred(context, auth_context, ppcreds, &out_data,
95 : replay_data);
96 0 : if (ret == 0) {
97 : /*
98 : * MIT allocates the return structure for no good reason. We do
99 : * likewise as, in this case, incompatibility is the greater evil.
100 : */
101 0 : *ppdata = calloc(1, sizeof(**ppdata));
102 0 : if (*ppdata) {
103 0 : **ppdata = out_data;
104 : } else {
105 0 : krb5_data_free(&out_data);
106 0 : ret = krb5_enomem(context);
107 : }
108 : }
109 :
110 0 : return ret;
111 : }
112 :
113 : /* ARGSUSED */
114 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
115 23028 : _krb5_mk_ncred(krb5_context context,
116 : krb5_auth_context auth_context,
117 : krb5_creds **ppcreds,
118 : krb5_data *out_data,
119 : krb5_replay_data *replay_data)
120 : {
121 1035 : krb5_error_code ret;
122 1035 : EncKrbCredPart enc_krb_cred_part;
123 1035 : KrbCredInfo *krb_cred_info;
124 1035 : krb5_crypto crypto;
125 1035 : KRB_CRED cred;
126 23028 : unsigned char *buf = NULL;
127 1035 : size_t ncreds, i;
128 1035 : size_t buf_size;
129 1035 : size_t len;
130 :
131 : /*
132 : * The ownership of 'buf' is re-assigned to a containing structure
133 : * multiple times. We enforce an invariant, either buf is non-zero
134 : * and we own it, or buf is zero and it is freed or some structure
135 : * owns any storage previously allocated as 'buf'.
136 : */
137 : #define CHOWN_BUF(x, buf) do { (x) = (buf); (buf) = 0; } while (0)
138 : #define DISOWN_BUF(buf) do { free(buf); (buf) = 0; } while (0)
139 :
140 46056 : for (ncreds = 0; ppcreds[ncreds]; ncreds++)
141 : ;
142 :
143 23028 : memset (&cred, 0, sizeof(cred));
144 23028 : memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
145 23028 : cred.pvno = 5;
146 23028 : cred.msg_type = krb_cred;
147 23028 : ALLOC_SEQ(&cred.tickets, ncreds);
148 23028 : if (cred.tickets.val == NULL) {
149 0 : ret = krb5_enomem(context);
150 0 : goto out;
151 : }
152 23028 : ALLOC_SEQ(&enc_krb_cred_part.ticket_info, ncreds);
153 23028 : if (enc_krb_cred_part.ticket_info.val == NULL) {
154 0 : ret = krb5_enomem(context);
155 0 : goto out;
156 : }
157 :
158 46056 : for (i = 0; i < ncreds; i++) {
159 24063 : ret = decode_Ticket(ppcreds[i]->ticket.data,
160 23028 : ppcreds[i]->ticket.length,
161 23028 : &cred.tickets.val[i],
162 : &len);/* don't care about len */
163 23028 : if (ret)
164 0 : goto out;
165 :
166 : /* fill ticket_info.val[i] */
167 23028 : krb_cred_info = &enc_krb_cred_part.ticket_info.val[i];
168 :
169 23028 : CHECKED_COPY(copy_EncryptionKey,
170 : &krb_cred_info->key, &ppcreds[i]->session);
171 23028 : CHECKED_ALLOC_COPY_PPC2KCI(copy_Realm, prealm, client->realm);
172 23028 : CHECKED_ALLOC_COPY_PPC2KCI(copy_PrincipalName, pname, client->name);
173 23028 : CHECKED_ALLOC_ASSIGN_PPC2KCI(flags, flags.b);
174 23028 : CHECKED_ALLOC_ASSIGN_PPC2KCI(authtime, times.authtime);
175 23028 : CHECKED_ALLOC_ASSIGN_PPC2KCI(starttime, times.starttime);
176 23028 : CHECKED_ALLOC_ASSIGN_PPC2KCI(endtime, times.endtime);
177 23028 : CHECKED_ALLOC_ASSIGN_PPC2KCI(renew_till, times.renew_till);
178 23028 : CHECKED_ALLOC_COPY_PPC2KCI(copy_Realm, srealm, server->realm);
179 23028 : CHECKED_ALLOC_COPY_PPC2KCI(copy_PrincipalName, sname, server->name);
180 23028 : CHECKED_ALLOC_COPY_PPC2KCI(copy_HostAddresses, caddr, addresses);
181 : }
182 :
183 23028 : if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
184 1035 : krb5_timestamp sec;
185 1035 : int32_t usec;
186 :
187 23028 : krb5_us_timeofday (context, &sec, &usec);
188 :
189 23028 : CHECKED_ALLOC_ASSIGN(enc_krb_cred_part.timestamp, sec);
190 23028 : CHECKED_ALLOC_ASSIGN(enc_krb_cred_part.usec, usec);
191 : } else {
192 0 : enc_krb_cred_part.timestamp = NULL;
193 0 : enc_krb_cred_part.usec = NULL;
194 : /* XXX Er, shouldn't we set the seq nums?? */
195 : }
196 :
197 : /* XXX: Is this needed? */
198 23028 : if (auth_context->local_address && auth_context->local_port) {
199 0 : ret = krb5_make_addrport(context,
200 : &enc_krb_cred_part.s_address,
201 0 : auth_context->local_address,
202 0 : auth_context->local_port);
203 0 : if (ret)
204 0 : goto out;
205 : }
206 :
207 : /* XXX: Is this needed? */
208 23028 : if (auth_context->remote_address) {
209 0 : if (auth_context->remote_port) {
210 : /*
211 : * XXX: Should we be checking "no-addresses" for
212 : * the receiving realm?
213 : */
214 0 : ret = krb5_make_addrport(context,
215 : &enc_krb_cred_part.r_address,
216 0 : auth_context->remote_address,
217 0 : auth_context->remote_port);
218 0 : if (ret)
219 0 : goto out;
220 : } else {
221 : /*
222 : * XXX Ugly, make krb5_make_addrport() handle missing port
223 : * number (i.e., port == 0), then remove this else.
224 : */
225 0 : CHECKED_ALLOC(enc_krb_cred_part.r_address);
226 0 : ret = krb5_copy_address(context, auth_context->remote_address,
227 0 : enc_krb_cred_part.r_address);
228 0 : if (ret)
229 0 : goto out;
230 : }
231 : }
232 :
233 : /* encode EncKrbCredPart */
234 23028 : ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size,
235 : &enc_krb_cred_part, &len, ret);
236 23028 : if (ret)
237 0 : goto out;
238 :
239 : /**
240 : * Some older of the MIT gssapi library used clear-text tickets
241 : * (warped inside AP-REQ encryption), use the krb5_auth_context
242 : * flag KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED to support those
243 : * tickets. The session key is used otherwise to encrypt the
244 : * forwarded ticket.
245 : */
246 :
247 23028 : if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) {
248 23028 : cred.enc_part.etype = KRB5_ENCTYPE_NULL;
249 23028 : cred.enc_part.kvno = NULL;
250 23028 : CHOWN_BUF(cred.enc_part.cipher.data, buf);
251 23028 : cred.enc_part.cipher.length = buf_size;
252 : } else {
253 : /*
254 : * Here older versions then 0.7.2 of Heimdal used the local or
255 : * remote subkey. That is wrong, the session key should be
256 : * used. Heimdal 0.7.2 and newer have code to try both in the
257 : * receiving end.
258 : */
259 :
260 0 : ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
261 0 : if (ret == 0)
262 0 : ret = krb5_encrypt_EncryptedData(context,
263 : crypto,
264 : KRB5_KU_KRB_CRED,
265 : buf,
266 : len,
267 : 0,
268 : &cred.enc_part);
269 0 : if (ret)
270 0 : goto out;
271 0 : DISOWN_BUF(buf);
272 0 : krb5_crypto_destroy(context, crypto);
273 : }
274 :
275 23028 : ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret);
276 23028 : if (ret)
277 0 : goto out;
278 :
279 23028 : CHOWN_BUF(out_data->data, buf);
280 23028 : out_data->length = len;
281 23028 : ret = 0;
282 :
283 23028 : out:
284 23028 : free_EncKrbCredPart(&enc_krb_cred_part);
285 23028 : free_KRB_CRED(&cred);
286 23028 : free(buf);
287 23028 : return ret;
288 : }
289 :
290 : /**
291 : * Make a KRB-CRED PDU with 1 credential.
292 : *
293 : * @param context A kerberos 5 context.
294 : * @param auth_context The auth context with the key to encrypt the out_data.
295 : * @param ppcred A credential to forward.
296 : * @param ppdata The output KRB-CRED (to be freed by caller).
297 : * @param replay_data (unused).
298 : *
299 : * @return Return an error code or 0.
300 : *
301 : * @ingroup krb5_credential
302 : */
303 :
304 : /* ARGSUSED */
305 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
306 0 : krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
307 : krb5_creds *ppcred, krb5_data **ppdata,
308 : krb5_replay_data *replay_data)
309 : {
310 0 : krb5_creds *ppcreds[2] = { ppcred, NULL };
311 :
312 0 : return krb5_mk_ncred(context, auth_context, ppcreds, ppdata, replay_data);
313 : }
314 :
315 : /* ARGSUSED */
316 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
317 23028 : _krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
318 : krb5_creds *ppcred, krb5_data *ppdata,
319 : krb5_replay_data *replay_data)
320 : {
321 23028 : krb5_creds *ppcreds[2] = { ppcred, NULL };
322 :
323 23028 : return _krb5_mk_ncred(context, auth_context, ppcreds, ppdata, replay_data);
324 : }
|