s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / source4 / heimdal / lib / gss_preauth / pa_common.c
1 /*
2  * Copyright (c) 2021, PADL Software Pty Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of PADL Software nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <krb5_locl.h>
34 #include <mech_locl.h>
35 #include <heimntlm.h>
36
37 #include "gss-preauth-protos.h"
38 #include "gss-preauth-private.h"
39
40 krb5_error_code
41 _krb5_gss_map_error(OM_uint32 major, OM_uint32 minor)
42 {
43     krb5_error_code ret;
44
45     if (minor != 0)
46         return (krb5_error_code)minor;
47
48     switch (major) {
49     case GSS_S_COMPLETE:
50         ret = 0;
51         break;
52     case GSS_S_CONTINUE_NEEDED:
53         ret = HEIM_ERR_PA_CONTINUE_NEEDED;
54         break;
55     case GSS_S_BAD_NAME:
56     case GSS_S_BAD_NAMETYPE:
57         ret = KRB5_PRINC_NOMATCH;
58         break;
59     case GSS_S_NO_CRED:
60         ret = KRB5_CC_NOTFOUND;
61         break;
62     case GSS_S_BAD_MIC:
63     case GSS_S_DEFECTIVE_CREDENTIAL:
64         ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
65         break;
66     case GSS_S_FAILURE:
67         if (minor == (OM_uint32)KRB5KRB_AP_ERR_BAD_INTEGRITY ||
68             minor == (OM_uint32)HNTLM_ERR_AUTH) {
69             ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
70             break;
71         }
72     default:
73         ret = KRB5KDC_ERR_PREAUTH_FAILED;
74         break;
75     }
76
77     return ret;
78 }
79
80 krb5_error_code
81 _krb5_gss_pa_derive_key(krb5_context context,
82                         gss_ctx_id_t ctx,
83                         krb5int32 nonce,
84                         krb5_enctype enctype,
85                         krb5_keyblock **keyblock)
86 {
87     krb5_error_code ret;
88     u_char saltdata[12] = "KRB-GSS";
89     krb5_keyblock kdkey;
90     size_t keysize;
91
92     OM_uint32 major, minor;
93     gss_buffer_desc salt, dkey = GSS_C_EMPTY_BUFFER;
94
95     *keyblock = NULL;
96
97     ret = krb5_enctype_keysize(context, enctype, &keysize);
98     if (ret)
99         return ret;
100
101     saltdata[ 8] = (nonce >> 0 ) & 0xFF;
102     saltdata[ 9] = (nonce >> 8 ) & 0xFF;
103     saltdata[10] = (nonce >> 16) & 0xFF;
104     saltdata[11] = (nonce >> 24) & 0xFF;
105
106     salt.value = saltdata;
107     salt.length = sizeof(saltdata);
108
109     major = gss_pseudo_random(&minor, ctx, GSS_C_PRF_KEY_FULL,
110                               &salt, keysize, &dkey);
111     if (GSS_ERROR(major))
112         return KRB5_PREAUTH_NO_KEY;
113
114     kdkey.keytype = enctype;
115     kdkey.keyvalue.data = dkey.value;
116     kdkey.keyvalue.length = dkey.length;
117
118     ret = krb5_copy_keyblock(context, &kdkey, keyblock);
119
120     if (dkey.value) {
121         memset_s(dkey.value, dkey.length, 0, dkey.length);
122         gss_release_buffer(&minor, &dkey);
123     }
124
125     return ret;
126 }
127
128 krb5_error_code
129 _krb5_gss_pa_unparse_name(krb5_context context,
130                           krb5_const_principal principal,
131                           gss_name_t *namep)
132 {
133     krb5_error_code ret;
134     char *name = NULL;
135
136     OM_uint32 major, minor;
137     gss_buffer_desc name_buf;
138
139     *namep = GSS_C_NO_NAME;
140
141     if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
142         if (principal->name.name_string.len != 1)
143             return EINVAL;
144
145         name = principal->name.name_string.val[0];
146     } else {
147         ret = krb5_unparse_name(context, principal, &name);
148         if (ret)
149             return ret;
150     }
151
152     name_buf.length = strlen(name);
153     name_buf.value = name;
154
155     major = gss_import_name(&minor, &name_buf,
156                             GSS_KRB5_NT_PRINCIPAL_NAME, namep);
157     if (major == GSS_S_BAD_NAMETYPE) {
158         gss_OID name_type = GSS_C_NO_OID;
159         int flags = 0;
160
161         if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
162             name_type = GSS_C_NT_USER_NAME;
163         } else if (principal->name.name_type == KRB5_NT_PRINCIPAL) {
164             flags = KRB5_PRINCIPAL_UNPARSE_SHORT;
165             name_type = GSS_C_NT_USER_NAME;
166         } else if ((principal->name.name_type == KRB5_NT_SRV_HST ||
167                     principal->name.name_type == KRB5_NT_SRV_INST) &&
168             principal->name.name_string.len == 2) {
169             flags = KRB5_PRINCIPAL_UNPARSE_NO_REALM;
170             name_type = GSS_C_NT_HOSTBASED_SERVICE;
171         }
172
173         if (flags) {
174             krb5_xfree(name);
175
176             ret = krb5_unparse_name_flags(context, principal, flags, &name);
177             if (ret)
178                 return ret;
179
180             if (gss_oid_equal(name_type, GSS_C_NT_HOSTBASED_SERVICE)) {
181                 char *inst = strchr(name, '/');
182                 if (inst)
183                     *inst = '@';
184             }
185
186             name_buf.length = strlen(name);
187             name_buf.value = name;
188         }
189
190         if (name_type)
191             major = gss_import_name(&minor, &name_buf, name_type, namep);
192     }
193
194     if (name != principal->name.name_string.val[0])
195         krb5_xfree(name);
196
197     return _krb5_gss_map_error(major, minor);
198 }
199
200 krb5_error_code
201 _krb5_gss_pa_parse_name(krb5_context context,
202                         gss_const_name_t name,
203                         int flags,
204                         krb5_principal *principal)
205 {
206     krb5_error_code ret;
207     char *displayed_name0;
208
209     OM_uint32 major, minor;
210     gss_OID name_type = GSS_C_NO_OID;
211     gss_buffer_desc displayed_name = GSS_C_EMPTY_BUFFER;
212
213     major = gss_display_name(&minor, name, &displayed_name, &name_type);
214     if (GSS_ERROR(major))
215         return _krb5_gss_map_error(major, minor);
216
217     if (gss_oid_equal(name_type, GSS_C_NT_ANONYMOUS)) {
218         ret = krb5_make_principal(context, principal, KRB5_ANON_REALM,
219                                   KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
220         if (ret == 0)
221             (*principal)->name.name_type = KRB5_NT_WELLKNOWN;
222     } else {
223         displayed_name0 = malloc(displayed_name.length + 1);
224             if (displayed_name0 == NULL)
225                 return krb5_enomem(context);
226
227         memcpy(displayed_name0, displayed_name.value, displayed_name.length);
228         displayed_name0[displayed_name.length] = '\0';
229
230         ret = krb5_parse_name_flags(context, displayed_name0, flags, principal);
231                                     gss_release_buffer(&minor, &displayed_name);
232         free(displayed_name0);
233     }
234
235     gss_release_buffer(&minor, &displayed_name);
236
237     return ret;
238 }
239
240 void
241 _krb5_gss_data_to_buffer(const krb5_data *data, gss_buffer_t buffer)
242 {
243     if (data) {
244         buffer->length = data->length;
245         buffer->value = data->data;
246     } else {
247         _mg_buffer_zero(buffer);
248     }
249 }
250
251 void
252 _krb5_gss_buffer_to_data(gss_const_buffer_t buffer, krb5_data *data)
253 {
254     if (buffer) {
255         data->length = buffer->length;
256         data->data = buffer->value;
257     } else {
258         krb5_data_zero(data);
259     }
260 }