third_party/heimdal: import lorikeet-heimdal-202404171655 (commit 28a56d818074e049f03...
[samba.git] / third_party / heimdal / lib / krb5 / build_auth.c
1 /*
2  * Copyright (c) 1997 - 2003 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 static krb5_error_code
37 add_auth_data(krb5_context context,
38               AuthorizationData *src,
39               AuthorizationData **dst)
40 {
41     krb5_error_code ret = 0;
42     size_t i;
43
44     if (*dst == NULL &&
45         (*dst = calloc(1, sizeof(**dst))) == NULL)
46         return krb5_enomem(context);
47     for (i = 0; ret == 0 && i < src->len; i++)
48         ret = add_AuthorizationData(*dst, &src->val[i]);
49     return ret;
50 }
51
52 static krb5_error_code
53 add_etypelist(krb5_context context,
54               krb5_authdata *auth_data)
55 {
56     AuthorizationDataElement ade;
57     EtypeList etypes;
58     krb5_error_code ret;
59     krb5_data e;
60     size_t len = 0;
61
62     ret = _krb5_init_etype(context, KRB5_PDU_NONE,
63                            &etypes.len, &etypes.val,
64                            NULL);
65     if (ret)
66         return ret;
67
68     ASN1_MALLOC_ENCODE(EtypeList, e.data, e.length, &etypes, &len, ret);
69     if (ret) {
70         free_EtypeList(&etypes);
71         return ret;
72     }
73     if(e.length != len)
74         krb5_abortx(context, "internal error in ASN.1 encoder");
75     free_EtypeList(&etypes);
76
77     ade.ad_type = KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION;
78     ade.ad_data = e;
79
80     ret = add_AuthorizationData(auth_data, &ade);
81
82     krb5_data_free(&e);
83
84     return ret;
85 }
86
87 static krb5_error_code
88 add_ap_options(krb5_context context,
89                krb5_boolean channel_bound,
90                krb5_authdata *auth_data)
91 {
92     krb5_error_code ret;
93     AuthorizationDataElement ade;
94     krb5_boolean require_cb;
95     uint8_t ap_options[4];
96
97     require_cb = krb5_config_get_bool_default(context, NULL, FALSE,
98                                               "libdefaults",
99                                               "client_aware_channel_bindings",
100                                               NULL);
101
102     if (channel_bound)
103         require_cb = TRUE;
104
105     if (!require_cb)
106         return 0;
107
108     ap_options[0] = (KERB_AP_OPTIONS_CBT >> 0 ) & 0xFF;
109     ap_options[1] = (KERB_AP_OPTIONS_CBT >> 8 ) & 0xFF;
110     ap_options[2] = (KERB_AP_OPTIONS_CBT >> 16) & 0xFF;
111     ap_options[3] = (KERB_AP_OPTIONS_CBT >> 24) & 0xFF;
112
113     ade.ad_type = KRB5_AUTHDATA_AP_OPTIONS;
114     ade.ad_data.length = sizeof(ap_options);
115     ade.ad_data.data = ap_options;
116
117     ret = add_AuthorizationData(auth_data, &ade);
118
119     return ret;
120 }
121
122 static krb5_error_code
123 add_target_principal(krb5_context context,
124                      krb5_const_principal server,
125                      krb5_authdata *auth_data)
126 {
127     krb5_error_code ret;
128     AuthorizationDataElement ade;
129     char *s, *s2 = NULL;
130     size_t s2_len;
131
132     if (server == NULL) {
133         return 0;
134     }
135
136     ret = krb5_unparse_name_flags(context, server,
137                                   KRB5_PRINCIPAL_UNPARSE_DISPLAY,
138                                   &s);
139     if (ret)
140         return ret;
141
142     {
143         size_t ucs2_len;
144         uint16_t *ucs2;
145         unsigned int flags;
146
147         ret = wind_utf8ucs2_length(s, &ucs2_len);
148         if (ret) {
149             krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
150             free(s);
151             return ret;
152         }
153
154         ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len);
155         if (ucs2 == NULL) {
156             free(s);
157             return krb5_enomem(context);
158         }
159
160         ret = wind_utf8ucs2(s, ucs2, &ucs2_len);
161         if (ret) {
162             free(ucs2);
163             krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
164             free(s);
165             return ret;
166         } else
167             free(s);
168
169         s2_len = (ucs2_len + 1) * 2;
170         s2 = malloc(s2_len);
171         if (s2 == NULL) {
172             free(ucs2);
173             return krb5_enomem(context);
174         }
175
176         flags = WIND_RW_LE;
177         ret = wind_ucs2write(ucs2, ucs2_len,
178                              &flags, s2, &s2_len);
179         free(ucs2);
180         if (ret) {
181             free(s2);
182             krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer");
183             return ret;
184         }
185
186         /*
187          * we do not want zero termination
188          */
189         s2_len = ucs2_len * 2;
190     }
191
192     ade.ad_type = KRB5_AUTHDATA_TARGET_PRINCIPAL;
193     ade.ad_data.length = s2_len;
194     ade.ad_data.data = s2;
195
196     ret = add_AuthorizationData(auth_data, &ade);
197     free(s2);
198
199     return ret;
200 }
201
202 static krb5_error_code
203 make_ap_authdata(krb5_context context,
204                  krb5_boolean channel_bound,
205                  krb5_const_principal server,
206                  krb5_authdata **auth_data)
207 {
208     krb5_error_code ret;
209     AuthorizationData ad;
210     krb5_data ir;
211     size_t len;
212
213     ad.len = 0;
214     ad.val = NULL;
215
216     ret = add_etypelist(context, &ad);
217     if (ret)
218         return ret;
219
220     /*
221      * Windows has a bug and only looks for first occurrence of AD-IF-RELEVANT
222      * in the AP authenticator when looking for AD-AP-OPTIONS. Make sure to
223      * bundle it together with etypes.
224      */
225     ret = add_ap_options(context, channel_bound, &ad);
226     if (ret) {
227         free_AuthorizationData(&ad);
228         return ret;
229     }
230
231     ret = add_target_principal(context, server, &ad);
232     if (ret) {
233         free_AuthorizationData(&ad);
234         return ret;
235     }
236
237     ASN1_MALLOC_ENCODE(AuthorizationData, ir.data, ir.length, &ad, &len, ret);
238     if (ret) {
239         free_AuthorizationData(&ad);
240         return ret;
241     }
242     if(ir.length != len)
243         krb5_abortx(context, "internal error in ASN.1 encoder");
244
245     ret = _krb5_add_1auth_data(context, KRB5_AUTHDATA_IF_RELEVANT, &ir, 1,
246                                auth_data);
247
248     free_AuthorizationData(&ad);
249     krb5_data_free(&ir);
250
251     return ret;
252 }
253
254 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
255 _krb5_build_authenticator (krb5_context context,
256                            krb5_auth_context auth_context,
257                            krb5_enctype enctype,
258                            krb5_creds *cred,
259                            Checksum *cksum,
260                            krb5_boolean channel_bound,
261                            krb5_data *result,
262                            krb5_key_usage usage)
263 {
264     Authenticator auth;
265     u_char *buf = NULL;
266     size_t buf_size;
267     size_t len = 0;
268     krb5_error_code ret;
269     krb5_crypto crypto;
270
271     memset(&auth, 0, sizeof(auth));
272
273     auth.authenticator_vno = 5;
274     ret = copy_Realm(&cred->client->realm, &auth.crealm);
275     if (ret)
276         goto fail;
277     ret = copy_PrincipalName(&cred->client->name, &auth.cname);
278     if (ret)
279         goto fail;
280
281     krb5_us_timeofday (context, &auth.ctime, &auth.cusec);
282
283     ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth.subkey);
284     if(ret)
285         goto fail;
286
287     if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
288         if(auth_context->local_seqnumber == 0)
289             krb5_generate_seq_number (context,
290                                       &cred->session,
291                                       &auth_context->local_seqnumber);
292         ALLOC(auth.seq_number, 1);
293         if(auth.seq_number == NULL) {
294             ret = krb5_enomem(context);
295             goto fail;
296         }
297         *auth.seq_number = auth_context->local_seqnumber;
298     } else
299         auth.seq_number = NULL;
300     auth.authorization_data = NULL;
301
302     if (cksum) {
303         ALLOC(auth.cksum, 1);
304         if (auth.cksum == NULL) {
305             ret = krb5_enomem(context);
306             goto fail;
307         }
308         ret = copy_Checksum(cksum, auth.cksum);
309         if (ret)
310             goto fail;
311
312         if (auth.cksum->cksumtype == CKSUMTYPE_GSSAPI) {
313             /*
314              * This is not GSS-API specific, we only enable it for
315              * GSS for now
316              */
317             ret = make_ap_authdata(context,
318                                    channel_bound,
319                                    cred->server,
320                                    &auth.authorization_data);
321             if (ret)
322                 goto fail;
323         }
324     }
325
326     /* Copy other authz data from auth_context */
327     if (auth_context->auth_data) {
328         ret = add_auth_data(context, auth_context->auth_data, &auth.authorization_data);
329         if (ret)
330             goto fail;
331     }
332
333     /* XXX - Copy more to auth_context? */
334
335     auth_context->authenticator->ctime = auth.ctime;
336     auth_context->authenticator->cusec = auth.cusec;
337
338     ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, &auth, &len, ret);
339     if (ret)
340         goto fail;
341     if(buf_size != len)
342         krb5_abortx(context, "internal error in ASN.1 encoder");
343
344     ret = krb5_crypto_init(context, &cred->session, enctype, &crypto);
345     if (ret)
346         goto fail;
347     ret = krb5_encrypt (context,
348                         crypto,
349                         usage /* KRB5_KU_AP_REQ_AUTH */,
350                         buf,
351                         len,
352                         result);
353     krb5_crypto_destroy(context, crypto);
354
355     if (ret)
356         goto fail;
357
358  fail:
359     free_Authenticator (&auth);
360     free (buf);
361
362     return ret;
363 }