3e0012562a3c60fb8659dfa0695b3a53a4d2e316
[metze/samba/wip.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_authdata *auth_data)
90 {
91     krb5_error_code ret;
92     AuthorizationDataElement ade;
93     krb5_boolean require_cb;
94     uint8_t ap_options[4];
95
96     require_cb = krb5_config_get_bool_default(context, NULL, FALSE,
97                                               "libdefaults",
98                                               "client_aware_channel_bindings",
99                                               NULL);
100
101     if (!require_cb)
102         return 0;
103
104     ap_options[0] = (KERB_AP_OPTIONS_CBT >> 0 ) & 0xFF;
105     ap_options[1] = (KERB_AP_OPTIONS_CBT >> 8 ) & 0xFF;
106     ap_options[2] = (KERB_AP_OPTIONS_CBT >> 16) & 0xFF;
107     ap_options[3] = (KERB_AP_OPTIONS_CBT >> 24) & 0xFF;
108
109     ade.ad_type = KRB5_AUTHDATA_AP_OPTIONS;
110     ade.ad_data.length = sizeof(ap_options);
111     ade.ad_data.data = ap_options;
112
113     ret = add_AuthorizationData(auth_data, &ade);
114
115     return ret;
116 }
117
118 static krb5_error_code
119 make_ap_authdata(krb5_context context,
120                  krb5_authdata **auth_data)
121 {
122     krb5_error_code ret;
123     AuthorizationData ad;
124     krb5_data ir;
125     size_t len;
126
127     ad.len = 0;
128     ad.val = NULL;
129
130     ret = add_etypelist(context, &ad);
131     if (ret)
132         return ret;
133
134     /*
135      * Windows has a bug and only looks for first occurrence of AD-IF-RELEVANT
136      * in the AP authenticator when looking for AD-AP-OPTIONS. Make sure to
137      * bundle it together with etypes.
138      */
139     ret = add_ap_options(context, &ad);
140     if (ret) {
141         free_AuthorizationData(&ad);
142         return ret;
143     }
144
145     ASN1_MALLOC_ENCODE(AuthorizationData, ir.data, ir.length, &ad, &len, ret);
146     if (ret) {
147         free_AuthorizationData(&ad);
148         return ret;
149     }
150     if(ir.length != len)
151         krb5_abortx(context, "internal error in ASN.1 encoder");
152
153     ret = _krb5_add_1auth_data(context, KRB5_AUTHDATA_IF_RELEVANT, &ir, 1,
154                                auth_data);
155
156     free_AuthorizationData(&ad);
157     krb5_data_free(&ir);
158
159     return ret;
160 }
161
162 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
163 _krb5_build_authenticator (krb5_context context,
164                            krb5_auth_context auth_context,
165                            krb5_enctype enctype,
166                            krb5_creds *cred,
167                            Checksum *cksum,
168                            krb5_data *result,
169                            krb5_key_usage usage)
170 {
171     Authenticator auth;
172     u_char *buf = NULL;
173     size_t buf_size;
174     size_t len = 0;
175     krb5_error_code ret;
176     krb5_crypto crypto;
177
178     memset(&auth, 0, sizeof(auth));
179
180     auth.authenticator_vno = 5;
181     ret = copy_Realm(&cred->client->realm, &auth.crealm);
182     if (ret)
183         goto fail;
184     ret = copy_PrincipalName(&cred->client->name, &auth.cname);
185     if (ret)
186         goto fail;
187
188     krb5_us_timeofday (context, &auth.ctime, &auth.cusec);
189
190     ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth.subkey);
191     if(ret)
192         goto fail;
193
194     if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
195         if(auth_context->local_seqnumber == 0)
196             krb5_generate_seq_number (context,
197                                       &cred->session,
198                                       &auth_context->local_seqnumber);
199         ALLOC(auth.seq_number, 1);
200         if(auth.seq_number == NULL) {
201             ret = krb5_enomem(context);
202             goto fail;
203         }
204         *auth.seq_number = auth_context->local_seqnumber;
205     } else
206         auth.seq_number = NULL;
207     auth.authorization_data = NULL;
208
209     if (cksum) {
210         ALLOC(auth.cksum, 1);
211         if (auth.cksum == NULL) {
212             ret = krb5_enomem(context);
213             goto fail;
214         }
215         ret = copy_Checksum(cksum, auth.cksum);
216         if (ret)
217             goto fail;
218
219         if (auth.cksum->cksumtype == CKSUMTYPE_GSSAPI) {
220             /*
221              * This is not GSS-API specific, we only enable it for
222              * GSS for now
223              */
224             ret = make_ap_authdata(context, &auth.authorization_data);
225             if (ret)
226                 goto fail;
227         }
228     }
229
230     /* Copy other authz data from auth_context */
231     if (auth_context->auth_data) {
232         ret = add_auth_data(context, auth_context->auth_data, &auth.authorization_data);
233         if (ret)
234             goto fail;
235     }
236
237     /* XXX - Copy more to auth_context? */
238
239     auth_context->authenticator->ctime = auth.ctime;
240     auth_context->authenticator->cusec = auth.cusec;
241
242     ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, &auth, &len, ret);
243     if (ret)
244         goto fail;
245     if(buf_size != len)
246         krb5_abortx(context, "internal error in ASN.1 encoder");
247
248     ret = krb5_crypto_init(context, &cred->session, enctype, &crypto);
249     if (ret)
250         goto fail;
251     ret = krb5_encrypt (context,
252                         crypto,
253                         usage /* KRB5_KU_AP_REQ_AUTH */,
254                         buf,
255                         len,
256                         result);
257     krb5_crypto_destroy(context, crypto);
258
259     if (ret)
260         goto fail;
261
262  fail:
263     free_Authenticator (&auth);
264     free (buf);
265
266     return ret;
267 }