/src/samba/auth/kerberos/gssapi_helper.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | GSSAPI helper functions |
4 | | |
5 | | Copyright (C) Stefan Metzmacher 2008,2015 |
6 | | |
7 | | This program is free software; you can redistribute it and/or modify |
8 | | it under the terms of the GNU General Public License as published by |
9 | | the Free Software Foundation; either version 3 of the License, or |
10 | | (at your option) any later version. |
11 | | |
12 | | This program is distributed in the hope that it will be useful, |
13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | GNU General Public License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include "includes.h" |
22 | | #include "system/gssapi.h" |
23 | | #include "auth/kerberos/pac_utils.h" |
24 | | #include "auth/kerberos/gssapi_helper.h" |
25 | | |
26 | | #undef DBGC_CLASS |
27 | 0 | #define DBGC_CLASS DBGC_AUTH |
28 | | |
29 | | size_t gssapi_get_sig_size(gss_ctx_id_t gssapi_context, |
30 | | const gss_OID mech, |
31 | | uint32_t gss_want_flags, |
32 | | size_t data_size) |
33 | 0 | { |
34 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
35 | 0 | size_t sig_size = 0; |
36 | |
|
37 | 0 | if (gss_want_flags & GSS_C_CONF_FLAG) { |
38 | 0 | OM_uint32 min_stat, maj_stat; |
39 | 0 | bool want_sealing = true; |
40 | 0 | int sealed = 0; |
41 | 0 | gss_iov_buffer_desc iov[2]; |
42 | |
|
43 | 0 | if (!(gss_want_flags & GSS_C_DCE_STYLE)) { |
44 | 0 | TALLOC_FREE(frame); |
45 | 0 | return 0; |
46 | 0 | } |
47 | | |
48 | | /* |
49 | | * gss_wrap_iov_length() only needs the type and length |
50 | | */ |
51 | 0 | iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; |
52 | 0 | iov[0].buffer.value = NULL; |
53 | 0 | iov[0].buffer.length = 0; |
54 | 0 | iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; |
55 | 0 | iov[1].buffer.value = NULL; |
56 | 0 | iov[1].buffer.length = data_size; |
57 | |
|
58 | 0 | maj_stat = gss_wrap_iov_length(&min_stat, |
59 | 0 | gssapi_context, |
60 | 0 | want_sealing, |
61 | 0 | GSS_C_QOP_DEFAULT, |
62 | 0 | &sealed, |
63 | 0 | iov, ARRAY_SIZE(iov)); |
64 | 0 | if (maj_stat) { |
65 | 0 | DEBUG(0, ("gss_wrap_iov_length failed with [%s]\n", |
66 | 0 | gssapi_error_string(frame, |
67 | 0 | maj_stat, |
68 | 0 | min_stat, |
69 | 0 | mech))); |
70 | 0 | TALLOC_FREE(frame); |
71 | 0 | return 0; |
72 | 0 | } |
73 | | |
74 | 0 | sig_size = iov[0].buffer.length; |
75 | 0 | } else if (gss_want_flags & GSS_C_INTEG_FLAG) { |
76 | 0 | NTSTATUS status; |
77 | 0 | uint32_t keytype; |
78 | |
|
79 | 0 | status = gssapi_get_session_key(frame, |
80 | 0 | gssapi_context, |
81 | 0 | NULL, &keytype); |
82 | 0 | if (!NT_STATUS_IS_OK(status)) { |
83 | 0 | TALLOC_FREE(frame); |
84 | 0 | return 0; |
85 | 0 | } |
86 | | |
87 | 0 | switch (keytype) { |
88 | 0 | case ENCTYPE_DES_CBC_MD5: |
89 | 0 | case ENCTYPE_DES_CBC_CRC: |
90 | 0 | case ENCTYPE_ARCFOUR_HMAC: |
91 | 0 | case ENCTYPE_ARCFOUR_HMAC_EXP: |
92 | 0 | sig_size = 37; |
93 | 0 | break; |
94 | 0 | default: |
95 | 0 | sig_size = 28; |
96 | 0 | break; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | TALLOC_FREE(frame); |
101 | 0 | return sig_size; |
102 | 0 | } |
103 | | |
104 | | NTSTATUS gssapi_seal_packet(gss_ctx_id_t gssapi_context, |
105 | | const gss_OID mech, |
106 | | bool hdr_signing, size_t sig_size, |
107 | | uint8_t *data, size_t length, |
108 | | const uint8_t *whole_pdu, size_t pdu_length, |
109 | | TALLOC_CTX *mem_ctx, |
110 | | DATA_BLOB *sig) |
111 | 0 | { |
112 | 0 | OM_uint32 maj_stat, min_stat; |
113 | 0 | gss_iov_buffer_desc iov[4]; |
114 | 0 | int req_seal = 1; |
115 | 0 | int sealed = 0; |
116 | 0 | const uint8_t *pre_sign_ptr = NULL; |
117 | 0 | size_t pre_sign_len = 0; |
118 | 0 | const uint8_t *post_sign_ptr = NULL; |
119 | 0 | size_t post_sign_len = 0; |
120 | |
|
121 | 0 | if (hdr_signing) { |
122 | 0 | const uint8_t *de = data + length; |
123 | 0 | const uint8_t *we = whole_pdu + pdu_length; |
124 | |
|
125 | 0 | if (data < whole_pdu) { |
126 | 0 | return NT_STATUS_INVALID_PARAMETER; |
127 | 0 | } |
128 | | |
129 | 0 | if (de > we) { |
130 | 0 | return NT_STATUS_INVALID_PARAMETER; |
131 | 0 | } |
132 | | |
133 | 0 | pre_sign_len = data - whole_pdu; |
134 | 0 | if (pre_sign_len > 0) { |
135 | 0 | pre_sign_ptr = whole_pdu; |
136 | 0 | } |
137 | 0 | post_sign_len = we - de; |
138 | 0 | if (post_sign_len > 0) { |
139 | 0 | post_sign_ptr = de; |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | 0 | sig->length = sig_size; |
144 | 0 | if (sig->length == 0) { |
145 | 0 | return NT_STATUS_ACCESS_DENIED; |
146 | 0 | } |
147 | | |
148 | 0 | sig->data = talloc_zero_array(mem_ctx, uint8_t, sig->length); |
149 | 0 | if (sig->data == NULL) { |
150 | 0 | return NT_STATUS_NO_MEMORY; |
151 | 0 | } |
152 | | |
153 | 0 | iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; |
154 | 0 | iov[0].buffer.length = sig->length; |
155 | 0 | iov[0].buffer.value = sig->data; |
156 | |
|
157 | 0 | if (pre_sign_ptr != NULL) { |
158 | 0 | iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; |
159 | 0 | iov[1].buffer.length = pre_sign_len; |
160 | 0 | iov[1].buffer.value = discard_const(pre_sign_ptr); |
161 | 0 | } else { |
162 | 0 | iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY; |
163 | 0 | iov[1].buffer.length = 0; |
164 | 0 | iov[1].buffer.value = NULL; |
165 | 0 | } |
166 | | |
167 | | /* data is encrypted in place, which is ok */ |
168 | 0 | iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; |
169 | 0 | iov[2].buffer.length = length; |
170 | 0 | iov[2].buffer.value = data; |
171 | |
|
172 | 0 | if (post_sign_ptr != NULL) { |
173 | 0 | iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; |
174 | 0 | iov[3].buffer.length = post_sign_len; |
175 | 0 | iov[3].buffer.value = discard_const(post_sign_ptr); |
176 | 0 | } else { |
177 | 0 | iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY; |
178 | 0 | iov[3].buffer.length = 0; |
179 | 0 | iov[3].buffer.value = NULL; |
180 | 0 | } |
181 | |
|
182 | 0 | maj_stat = gss_wrap_iov(&min_stat, |
183 | 0 | gssapi_context, |
184 | 0 | req_seal, |
185 | 0 | GSS_C_QOP_DEFAULT, |
186 | 0 | &sealed, |
187 | 0 | iov, ARRAY_SIZE(iov)); |
188 | 0 | if (GSS_ERROR(maj_stat)) { |
189 | 0 | char *error_string = gssapi_error_string(mem_ctx, |
190 | 0 | maj_stat, |
191 | 0 | min_stat, |
192 | 0 | mech); |
193 | 0 | DEBUG(1, ("gss_wrap_iov failed: %s\n", error_string)); |
194 | 0 | talloc_free(error_string); |
195 | 0 | data_blob_free(sig); |
196 | 0 | return NT_STATUS_ACCESS_DENIED; |
197 | 0 | } |
198 | | |
199 | 0 | if (req_seal == 1 && sealed == 0) { |
200 | 0 | DEBUG(0, ("gss_wrap_iov says data was not sealed!\n")); |
201 | 0 | data_blob_free(sig); |
202 | 0 | return NT_STATUS_ACCESS_DENIED; |
203 | 0 | } |
204 | | |
205 | 0 | dump_data_pw("gssapi_seal_packet: sig\n", sig->data, sig->length); |
206 | 0 | dump_data_pw("gssapi_seal_packet: sealed\n", data, length); |
207 | |
|
208 | 0 | DEBUG(10, ("Sealed %d bytes, and got %d bytes header/signature.\n", |
209 | 0 | (int)iov[2].buffer.length, (int)iov[0].buffer.length)); |
210 | |
|
211 | 0 | return NT_STATUS_OK; |
212 | 0 | } |
213 | | |
214 | | NTSTATUS gssapi_unseal_packet(gss_ctx_id_t gssapi_context, |
215 | | const gss_OID mech, |
216 | | bool hdr_signing, |
217 | | uint8_t *data, size_t length, |
218 | | const uint8_t *whole_pdu, size_t pdu_length, |
219 | | const DATA_BLOB *sig) |
220 | 0 | { |
221 | 0 | OM_uint32 maj_stat, min_stat; |
222 | 0 | gss_iov_buffer_desc iov[4]; |
223 | 0 | gss_qop_t qop_state; |
224 | 0 | int sealed = 0; |
225 | 0 | const uint8_t *pre_sign_ptr = NULL; |
226 | 0 | size_t pre_sign_len = 0; |
227 | 0 | const uint8_t *post_sign_ptr = NULL; |
228 | 0 | size_t post_sign_len = 0; |
229 | |
|
230 | 0 | if (hdr_signing) { |
231 | 0 | const uint8_t *de = data + length; |
232 | 0 | const uint8_t *we = whole_pdu + pdu_length; |
233 | |
|
234 | 0 | if (data < whole_pdu) { |
235 | 0 | return NT_STATUS_INVALID_PARAMETER; |
236 | 0 | } |
237 | | |
238 | 0 | if (de > we) { |
239 | 0 | return NT_STATUS_INVALID_PARAMETER; |
240 | 0 | } |
241 | | |
242 | 0 | pre_sign_len = data - whole_pdu; |
243 | 0 | if (pre_sign_len > 0) { |
244 | 0 | pre_sign_ptr = whole_pdu; |
245 | 0 | } |
246 | 0 | post_sign_len = we - de; |
247 | 0 | if (post_sign_len > 0) { |
248 | 0 | post_sign_ptr = de; |
249 | 0 | } |
250 | 0 | } |
251 | | |
252 | 0 | dump_data_pw("gssapi_unseal_packet: sig\n", sig->data, sig->length); |
253 | 0 | dump_data_pw("gssapi_unseal_packet: sealed\n", data, length); |
254 | |
|
255 | 0 | iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER; |
256 | 0 | iov[0].buffer.length = sig->length; |
257 | 0 | iov[0].buffer.value = sig->data; |
258 | |
|
259 | 0 | if (pre_sign_ptr != NULL) { |
260 | 0 | iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; |
261 | 0 | iov[1].buffer.length = pre_sign_len; |
262 | 0 | iov[1].buffer.value = discard_const(pre_sign_ptr); |
263 | 0 | } else { |
264 | 0 | iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY; |
265 | 0 | iov[1].buffer.length = 0; |
266 | 0 | iov[1].buffer.value = NULL; |
267 | 0 | } |
268 | | |
269 | | /* data is encrypted in place, which is ok */ |
270 | 0 | iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; |
271 | 0 | iov[2].buffer.length = length; |
272 | 0 | iov[2].buffer.value = data; |
273 | |
|
274 | 0 | if (post_sign_ptr != NULL) { |
275 | 0 | iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; |
276 | 0 | iov[3].buffer.length = post_sign_len; |
277 | 0 | iov[3].buffer.value = discard_const(post_sign_ptr); |
278 | 0 | } else { |
279 | 0 | iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY; |
280 | 0 | iov[3].buffer.length = 0; |
281 | 0 | iov[3].buffer.value = NULL; |
282 | 0 | } |
283 | |
|
284 | 0 | maj_stat = gss_unwrap_iov(&min_stat, |
285 | 0 | gssapi_context, |
286 | 0 | &sealed, |
287 | 0 | &qop_state, |
288 | 0 | iov, ARRAY_SIZE(iov)); |
289 | 0 | if (GSS_ERROR(maj_stat)) { |
290 | 0 | char *error_string = gssapi_error_string(NULL, |
291 | 0 | maj_stat, |
292 | 0 | min_stat, |
293 | 0 | mech); |
294 | 0 | DEBUG(1, ("gss_unwrap_iov failed: %s\n", error_string)); |
295 | 0 | talloc_free(error_string); |
296 | |
|
297 | 0 | return NT_STATUS_ACCESS_DENIED; |
298 | 0 | } |
299 | | |
300 | 0 | if (sealed == 0) { |
301 | 0 | DEBUG(0, ("gss_unwrap_iov says data was not sealed!\n")); |
302 | 0 | return NT_STATUS_ACCESS_DENIED; |
303 | 0 | } |
304 | | |
305 | 0 | DEBUG(10, ("Unsealed %d bytes, with %d bytes header/signature.\n", |
306 | 0 | (int)iov[2].buffer.length, (int)iov[0].buffer.length)); |
307 | |
|
308 | 0 | return NT_STATUS_OK; |
309 | 0 | } |
310 | | |
311 | | NTSTATUS gssapi_sign_packet(gss_ctx_id_t gssapi_context, |
312 | | const gss_OID mech, |
313 | | bool hdr_signing, |
314 | | const uint8_t *data, size_t length, |
315 | | const uint8_t *whole_pdu, size_t pdu_length, |
316 | | TALLOC_CTX *mem_ctx, |
317 | | DATA_BLOB *sig) |
318 | 0 | { |
319 | 0 | OM_uint32 maj_stat, min_stat; |
320 | 0 | gss_buffer_desc input_token, output_token; |
321 | |
|
322 | 0 | if (hdr_signing) { |
323 | 0 | input_token.length = pdu_length; |
324 | 0 | input_token.value = discard_const_p(uint8_t *, whole_pdu); |
325 | 0 | } else { |
326 | 0 | input_token.length = length; |
327 | 0 | input_token.value = discard_const_p(uint8_t *, data); |
328 | 0 | } |
329 | |
|
330 | 0 | maj_stat = gss_get_mic(&min_stat, |
331 | 0 | gssapi_context, |
332 | 0 | GSS_C_QOP_DEFAULT, |
333 | 0 | &input_token, |
334 | 0 | &output_token); |
335 | 0 | if (GSS_ERROR(maj_stat)) { |
336 | 0 | char *error_string = gssapi_error_string(mem_ctx, |
337 | 0 | maj_stat, |
338 | 0 | min_stat, |
339 | 0 | mech); |
340 | 0 | DEBUG(1, ("GSS GetMic failed: %s\n", error_string)); |
341 | 0 | talloc_free(error_string); |
342 | 0 | return NT_STATUS_ACCESS_DENIED; |
343 | 0 | } |
344 | | |
345 | 0 | *sig = data_blob_talloc(mem_ctx, (uint8_t *)output_token.value, output_token.length); |
346 | 0 | gss_release_buffer(&min_stat, &output_token); |
347 | 0 | if (sig->data == NULL) { |
348 | 0 | return NT_STATUS_NO_MEMORY; |
349 | 0 | } |
350 | | |
351 | 0 | dump_data_pw("gssapi_sign_packet: sig\n", sig->data, sig->length); |
352 | |
|
353 | 0 | return NT_STATUS_OK; |
354 | 0 | } |
355 | | |
356 | | NTSTATUS gssapi_check_packet(gss_ctx_id_t gssapi_context, |
357 | | const gss_OID mech, |
358 | | bool hdr_signing, |
359 | | const uint8_t *data, size_t length, |
360 | | const uint8_t *whole_pdu, size_t pdu_length, |
361 | | const DATA_BLOB *sig) |
362 | 0 | { |
363 | 0 | OM_uint32 maj_stat, min_stat; |
364 | 0 | gss_buffer_desc input_token; |
365 | 0 | gss_buffer_desc input_message; |
366 | 0 | gss_qop_t qop_state; |
367 | |
|
368 | 0 | dump_data_pw("gssapi_check_packet: sig\n", sig->data, sig->length); |
369 | |
|
370 | 0 | if (hdr_signing) { |
371 | 0 | input_message.length = pdu_length; |
372 | 0 | input_message.value = discard_const(whole_pdu); |
373 | 0 | } else { |
374 | 0 | input_message.length = length; |
375 | 0 | input_message.value = discard_const(data); |
376 | 0 | } |
377 | |
|
378 | 0 | input_token.length = sig->length; |
379 | 0 | input_token.value = sig->data; |
380 | |
|
381 | 0 | maj_stat = gss_verify_mic(&min_stat, |
382 | 0 | gssapi_context, |
383 | 0 | &input_message, |
384 | 0 | &input_token, |
385 | 0 | &qop_state); |
386 | 0 | if (GSS_ERROR(maj_stat)) { |
387 | 0 | char *error_string = gssapi_error_string(NULL, |
388 | 0 | maj_stat, |
389 | 0 | min_stat, |
390 | 0 | mech); |
391 | 0 | DEBUG(1, ("GSS VerifyMic failed: %s\n", error_string)); |
392 | 0 | talloc_free(error_string); |
393 | |
|
394 | 0 | return NT_STATUS_ACCESS_DENIED; |
395 | 0 | } |
396 | | |
397 | 0 | return NT_STATUS_OK; |
398 | 0 | } |