s4:heimdal: import lorikeet-heimdal-202201172009 (commit 5a0b45cd723628b3690ea848548b...
[samba.git] / source4 / heimdal / lib / krb5 / mk_cred.c
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 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     krb5_error_code ret;
92     krb5_data out_data;
93
94     ret = _krb5_mk_ncred(context, auth_context, ppcreds, &out_data,
95         replay_data);
96     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         *ppdata = calloc(1, sizeof(**ppdata));
102         if (*ppdata) {
103             **ppdata = out_data;
104         } else {
105             krb5_data_free(&out_data);
106             ret = krb5_enomem(context);
107         }
108     }
109
110     return ret;
111 }
112
113 /* ARGSUSED */
114 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
115 _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     krb5_error_code ret;
122     EncKrbCredPart enc_krb_cred_part;
123     KrbCredInfo *krb_cred_info;
124     krb5_crypto crypto;
125     KRB_CRED cred;
126     unsigned char *buf = NULL;
127     size_t ncreds, i;
128     size_t buf_size;
129     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     for (ncreds = 0; ppcreds[ncreds]; ncreds++)
141         ;
142
143     memset (&cred, 0, sizeof(cred));
144     memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
145     cred.pvno = 5;
146     cred.msg_type = krb_cred;
147     ALLOC_SEQ(&cred.tickets, ncreds);
148     if (cred.tickets.val == NULL) {
149         ret = krb5_enomem(context);
150         goto out;
151     }
152     ALLOC_SEQ(&enc_krb_cred_part.ticket_info, ncreds);
153     if (enc_krb_cred_part.ticket_info.val == NULL) {
154         ret = krb5_enomem(context);
155         goto out;
156     }
157
158     for (i = 0; i < ncreds; i++) {
159         ret = decode_Ticket(ppcreds[i]->ticket.data,
160                             ppcreds[i]->ticket.length,
161                             &cred.tickets.val[i],
162                             &len);/* don't care about len */
163         if (ret)
164            goto out;
165
166         /* fill ticket_info.val[i] */
167         krb_cred_info = &enc_krb_cred_part.ticket_info.val[i];
168
169         CHECKED_COPY(copy_EncryptionKey,
170                      &krb_cred_info->key, &ppcreds[i]->session);
171         CHECKED_ALLOC_COPY_PPC2KCI(copy_Realm, prealm, client->realm);
172         CHECKED_ALLOC_COPY_PPC2KCI(copy_PrincipalName, pname, client->name);
173         CHECKED_ALLOC_ASSIGN_PPC2KCI(flags, flags.b);
174         CHECKED_ALLOC_ASSIGN_PPC2KCI(authtime, times.authtime);
175         CHECKED_ALLOC_ASSIGN_PPC2KCI(starttime, times.starttime);
176         CHECKED_ALLOC_ASSIGN_PPC2KCI(endtime, times.endtime);
177         CHECKED_ALLOC_ASSIGN_PPC2KCI(renew_till, times.renew_till);
178         CHECKED_ALLOC_COPY_PPC2KCI(copy_Realm, srealm, server->realm);
179         CHECKED_ALLOC_COPY_PPC2KCI(copy_PrincipalName, sname, server->name);
180         CHECKED_ALLOC_COPY_PPC2KCI(copy_HostAddresses, caddr, addresses);
181     }
182
183     if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
184         krb5_timestamp sec;
185         int32_t usec;
186
187         krb5_us_timeofday (context, &sec, &usec);
188
189         CHECKED_ALLOC_ASSIGN(enc_krb_cred_part.timestamp, sec);
190         CHECKED_ALLOC_ASSIGN(enc_krb_cred_part.usec, usec);
191     } else {
192         enc_krb_cred_part.timestamp = NULL;
193         enc_krb_cred_part.usec = NULL;
194         /* XXX Er, shouldn't we set the seq nums?? */
195     }
196
197     /* XXX: Is this needed? */
198     if (auth_context->local_address && auth_context->local_port) {
199         ret = krb5_make_addrport(context,
200                                  &enc_krb_cred_part.s_address,
201                                  auth_context->local_address,
202                                  auth_context->local_port);
203         if (ret)
204             goto out;
205     }
206
207     /* XXX: Is this needed? */
208     if (auth_context->remote_address) {
209         if (auth_context->remote_port) {
210             /*
211              * XXX: Should we be checking "no-addresses" for
212              * the receiving realm?
213              */
214             ret = krb5_make_addrport(context,
215                                      &enc_krb_cred_part.r_address,
216                                      auth_context->remote_address,
217                                      auth_context->remote_port);
218             if (ret)
219                 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             CHECKED_ALLOC(enc_krb_cred_part.r_address);
226             ret = krb5_copy_address(context, auth_context->remote_address,
227                                     enc_krb_cred_part.r_address);
228             if (ret)
229                 goto out;
230         }
231     }
232
233     /* encode EncKrbCredPart */
234     ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size,
235                        &enc_krb_cred_part, &len, ret);
236     if (ret)
237         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     if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) {
248         cred.enc_part.etype = KRB5_ENCTYPE_NULL;
249         cred.enc_part.kvno = NULL;
250         CHOWN_BUF(cred.enc_part.cipher.data, buf);
251         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         ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
261         if (ret)
262             goto out;
263         ret = krb5_encrypt_EncryptedData(context,
264                                          crypto,
265                                          KRB5_KU_KRB_CRED,
266                                          buf,
267                                          len,
268                                          0,
269                                          &cred.enc_part);
270         DISOWN_BUF(buf);
271         krb5_crypto_destroy(context, crypto);
272     }
273
274     ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret);
275     if (ret)
276         goto out;
277
278     CHOWN_BUF(out_data->data, buf);
279     out_data->length = len;
280     ret = 0;
281
282  out:
283     free_EncKrbCredPart(&enc_krb_cred_part);
284     free_KRB_CRED(&cred);
285     free(buf);
286     return ret;
287 }
288
289 /**
290  * Make a KRB-CRED PDU with 1 credential.
291  *
292  * @param context A kerberos 5 context.
293  * @param auth_context The auth context with the key to encrypt the out_data.
294  * @param ppcred A credential to forward.
295  * @param ppdata The output KRB-CRED (to be freed by caller).
296  * @param replay_data (unused).
297  *
298  * @return Return an error code or 0.
299  *
300  * @ingroup krb5_credential
301  */
302
303 /* ARGSUSED */
304 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
305 krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
306               krb5_creds *ppcred, krb5_data **ppdata,
307               krb5_replay_data *replay_data)
308 {
309     krb5_creds *ppcreds[2] = { ppcred, NULL };
310
311     return krb5_mk_ncred(context, auth_context, ppcreds, ppdata, replay_data);
312 }
313
314 /* ARGSUSED */
315 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
316 _krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
317                krb5_creds *ppcred, krb5_data *ppdata,
318                krb5_replay_data *replay_data)
319 {
320     krb5_creds *ppcreds[2] = { ppcred, NULL };
321
322     return _krb5_mk_ncred(context, auth_context, ppcreds, ppdata, replay_data);
323 }