|
1 /* |
|
2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 // This library is client-side only, and only supports the default credentials. |
|
27 // It speaks krb5 and SPNEGO. NTLM is excluded from SPNEGO negotiation. |
|
28 // |
|
29 // This library can be built directly with the following command: |
|
30 // cl -I %OPENJDK%\src\java.security.jgss\share\native\libj2gss\ sspi.cpp \ |
|
31 // -link -dll -out:sspi_bridge.dll |
|
32 |
|
33 #define UNICODE |
|
34 #define _UNICODE |
|
35 |
|
36 #include <windows.h> |
|
37 #include <stdlib.h> |
|
38 #include <stdio.h> |
|
39 #include <string.h> |
|
40 #include <Strsafe.h> |
|
41 #include <ntsecapi.h> |
|
42 #include <new> |
|
43 |
|
44 #define GSS_DLL_FILE |
|
45 #include <gssapi.h> |
|
46 |
|
47 #define SECURITY_WIN32 |
|
48 #include <sspi.h> |
|
49 |
|
50 #pragma comment(lib, "secur32.lib") |
|
51 |
|
52 // Otherwise an exception will be thrown |
|
53 #define new new (std::nothrow) |
|
54 |
|
55 // A debugging macro |
|
56 #define PP(fmt, ...) \ |
|
57 if (trace) { \ |
|
58 fprintf(stderr, "[SSPI:%ld] "fmt"\n", __LINE__, ##__VA_ARGS__); \ |
|
59 fflush(stderr); \ |
|
60 } |
|
61 #define SEC_SUCCESS(status) ((*minor_status = (status)), (status) >= SEC_E_OK) |
|
62 |
|
63 #ifdef __cplusplus |
|
64 extern "C" { |
|
65 #endif /* __cplusplus */ |
|
66 |
|
67 // When SSPI_BRIDGE_TRACE is set, debug info goes to stderr. The value is ignored. |
|
68 char* trace = getenv("SSPI_BRIDGE_TRACE"); |
|
69 |
|
70 void |
|
71 dump(const char* title, PBYTE data, size_t len) |
|
72 { |
|
73 if (trace) { |
|
74 fprintf(stderr, "==== %s ====\n", title); |
|
75 for (size_t i = 0; i < len; i++) { |
|
76 if (i != 0 && i % 16 == 0) { |
|
77 fprintf(stderr, "\n"); |
|
78 } |
|
79 fprintf(stderr, "%02X ", *(data + i) & 0xff); |
|
80 } |
|
81 fprintf(stderr, "\n"); |
|
82 } |
|
83 } |
|
84 |
|
85 gss_OID_desc KRB5_OID = {9, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}; |
|
86 gss_OID_desc SPNEGO_OID = {6, (void*)"\x2b\x06\x01\x05\x05\x02"}; |
|
87 gss_OID_desc USER_NAME_OID = {10, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"}; |
|
88 gss_OID_desc KRB5_NAME_OID = {10, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"}; |
|
89 gss_OID_desc HOST_SERVICE_NAME_OID = {10, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"}; |
|
90 gss_OID_desc EXPORT_NAME_OID = {6, (void*)"\x2b\x06\x01\x05\x06\x04"}; |
|
91 |
|
92 struct gss_name_struct { |
|
93 SEC_WCHAR* name; |
|
94 }; |
|
95 |
|
96 struct gss_ctx_id_struct { |
|
97 CredHandle* phCred; |
|
98 CtxtHandle hCtxt; |
|
99 SecPkgContext_Sizes SecPkgContextSizes; |
|
100 SecPkgContext_NativeNames nnames; |
|
101 BOOLEAN established; |
|
102 BOOLEAN isSPNEGO; |
|
103 BOOLEAN isLocalCred; |
|
104 OM_uint32 flags; |
|
105 }; |
|
106 |
|
107 struct gss_cred_id_struct { |
|
108 CredHandle* phCredK; |
|
109 CredHandle* phCredS; |
|
110 long time; |
|
111 }; |
|
112 |
|
113 /* This section holds supporting functions that are not exported */ |
|
114 |
|
115 static OM_uint32 |
|
116 seconds_until(int inputIsUTC, TimeStamp *time) |
|
117 { |
|
118 // time is local time |
|
119 LARGE_INTEGER uiLocal; |
|
120 FILETIME now; |
|
121 GetSystemTimeAsFileTime(&now); |
|
122 if (!inputIsUTC) { |
|
123 FILETIME nowLocal; |
|
124 if (FileTimeToLocalFileTime(&now, &nowLocal) == 0) { |
|
125 return -1; |
|
126 } |
|
127 now = nowLocal; |
|
128 } |
|
129 uiLocal.HighPart = now.dwHighDateTime; |
|
130 uiLocal.LowPart = now.dwLowDateTime; |
|
131 if (time->QuadPart < uiLocal.QuadPart) { |
|
132 return 0; |
|
133 } |
|
134 ULONGLONG diff = (time->QuadPart - uiLocal.QuadPart) / 10000000; |
|
135 if (diff > (ULONGLONG)~(OM_uint32)0) |
|
136 return GSS_C_INDEFINITE; |
|
137 return (OM_uint32)diff; |
|
138 } |
|
139 |
|
140 static void |
|
141 show_time(char* label, TimeStamp* ts) |
|
142 { |
|
143 if (trace) { |
|
144 SYSTEMTIME stLocal; |
|
145 FileTimeToSystemTime((FILETIME*)ts, &stLocal); |
|
146 |
|
147 // Build a string showing the date and time. |
|
148 PP("%s: %02d/%02d/%d %02d:%02d %uld", label, |
|
149 stLocal.wMonth, stLocal.wDay, stLocal.wYear, |
|
150 stLocal.wHour, stLocal.wMinute, |
|
151 seconds_until(1, ts)); |
|
152 } |
|
153 } |
|
154 |
|
155 // isSPNEGO: true, SPNEGO. false, Kerberos. |
|
156 static gss_ctx_id_t |
|
157 new_context(BOOLEAN isSPNEGO) |
|
158 { |
|
159 gss_ctx_id_t out = new gss_ctx_id_struct; |
|
160 if (out == NULL) { |
|
161 return NULL; |
|
162 } |
|
163 out->phCred = NULL; |
|
164 out->hCtxt.dwLower = out->hCtxt.dwUpper = NULL; |
|
165 out->established = FALSE; |
|
166 out->SecPkgContextSizes.cbMaxSignature |
|
167 = out->SecPkgContextSizes.cbBlockSize |
|
168 = out->SecPkgContextSizes.cbSecurityTrailer |
|
169 = 0; |
|
170 out->nnames.sClientName = out->nnames.sServerName = NULL; |
|
171 out->isSPNEGO = isSPNEGO; |
|
172 out->isLocalCred = FALSE; |
|
173 return out; |
|
174 } |
|
175 |
|
176 static gss_cred_id_t |
|
177 new_cred() |
|
178 { |
|
179 gss_cred_id_t out = new gss_cred_id_struct; |
|
180 out->phCredK = out->phCredS = NULL; |
|
181 out->time = 0L; |
|
182 return out; |
|
183 } |
|
184 |
|
185 static int |
|
186 flag_sspi_to_gss(int fin) |
|
187 { |
|
188 int fout = 0; |
|
189 if (fin & ISC_REQ_MUTUAL_AUTH) fout |= GSS_C_MUTUAL_FLAG; |
|
190 if (fin & ISC_REQ_CONFIDENTIALITY) fout |= GSS_C_CONF_FLAG; |
|
191 if (fin & ISC_REQ_DELEGATE) fout |= GSS_C_DELEG_FLAG; |
|
192 if (fin & ISC_REQ_INTEGRITY) fout |= GSS_C_INTEG_FLAG; |
|
193 if (fin & ISC_REQ_REPLAY_DETECT) fout |= GSS_C_REPLAY_FLAG; |
|
194 if (fin & ISC_REQ_SEQUENCE_DETECT) fout |= GSS_C_SEQUENCE_FLAG; |
|
195 return fout; |
|
196 } |
|
197 |
|
198 static int |
|
199 flag_gss_to_sspi(int fin) |
|
200 { |
|
201 int fout = 0; |
|
202 if (fin & GSS_C_MUTUAL_FLAG) fout |= ISC_RET_MUTUAL_AUTH; |
|
203 if (fin & GSS_C_CONF_FLAG) fout |= ISC_RET_CONFIDENTIALITY; |
|
204 if (fin & GSS_C_DELEG_FLAG) fout |= ISC_RET_DELEGATE; |
|
205 if (fin & GSS_C_INTEG_FLAG) fout |= ISC_RET_INTEGRITY; |
|
206 if (fin & GSS_C_REPLAY_FLAG) fout |= ISC_RET_REPLAY_DETECT; |
|
207 if (fin & GSS_C_SEQUENCE_FLAG) fout |= ISC_RET_SEQUENCE_DETECT; |
|
208 return fout; |
|
209 } |
|
210 |
|
211 static BOOLEAN |
|
212 is_same_oid(gss_OID o2, gss_OID o1) |
|
213 { |
|
214 return o1 && o2 && o1->length == o2->length |
|
215 && !memcmp(o1->elements, o2->elements, o2->length); |
|
216 } |
|
217 |
|
218 static BOOLEAN |
|
219 has_oid(gss_OID_set set, gss_OID oid) |
|
220 { |
|
221 for (int i = 0; i < set->count; i++) { |
|
222 if (is_same_oid(&set->elements[i], oid)) { |
|
223 return TRUE; |
|
224 } |
|
225 } |
|
226 return FALSE; |
|
227 } |
|
228 |
|
229 static void |
|
230 get_oid_desc(gss_OID mech) |
|
231 { |
|
232 if (trace) { |
|
233 if (is_same_oid(mech, &KRB5_OID)) { |
|
234 PP("Kerberos mech"); |
|
235 } else if (is_same_oid(mech, &SPNEGO_OID)) { |
|
236 PP("SPNEGO mech"); |
|
237 } else if (is_same_oid(mech, &USER_NAME_OID)) { |
|
238 PP("NT_USER_NAME name-type"); |
|
239 } else if (is_same_oid(mech, &KRB5_NAME_OID)) { |
|
240 PP("KRB5_NAME name-type"); |
|
241 } else if (is_same_oid(mech, &HOST_SERVICE_NAME_OID)) { |
|
242 PP("NT_HOSTBASED_SERVICE name-type"); |
|
243 } else if (is_same_oid(mech, &EXPORT_NAME_OID)) { |
|
244 PP("NT_EXPORT_NAME name-type"); |
|
245 } else { |
|
246 dump("UNKNOWN OID", (PBYTE)mech->elements, mech->length); |
|
247 } |
|
248 } |
|
249 } |
|
250 |
|
251 static void |
|
252 get_oid_set_desc(gss_OID_set mechs) |
|
253 { |
|
254 if (trace) { |
|
255 if (mechs == NULL) { |
|
256 PP("OID set is NULL"); |
|
257 return; |
|
258 } |
|
259 PP("gss_OID_set.count is %d", (int)mechs->count); |
|
260 for (int i = 0; i < mechs->count; i++) { |
|
261 get_oid_desc(&mechs->elements[i]); |
|
262 } |
|
263 } |
|
264 } |
|
265 |
|
266 // Add realm to a name if there was none. |
|
267 // Returns a newly allocated name. |
|
268 static WCHAR* |
|
269 get_full_name(WCHAR* input) |
|
270 { |
|
271 // input has realm, no need to add one |
|
272 for (int i = 0;; i++) { |
|
273 if (!input[i]) { // the end |
|
274 break; |
|
275 } |
|
276 if (input[i] == L'\\') { // escaped |
|
277 i++; |
|
278 continue; |
|
279 } |
|
280 if (input[i] == L'@') { |
|
281 return _wcsdup(input); |
|
282 } |
|
283 } |
|
284 |
|
285 // Always use the default domain |
|
286 WCHAR* realm = _wgetenv(L"USERDNSDOMAIN"); |
|
287 if (realm == NULL) { |
|
288 realm = L""; |
|
289 } |
|
290 |
|
291 size_t oldlen = wcslen(input); |
|
292 size_t newlen = oldlen + 1 + wcslen(realm) + 1; |
|
293 |
|
294 WCHAR* fullname = new WCHAR[newlen]; |
|
295 if (!fullname) { |
|
296 return NULL; |
|
297 } |
|
298 wcscpy_s(fullname, newlen, input); |
|
299 wcscat_s(fullname, newlen, L"@"); |
|
300 wcscat_s(fullname, newlen, realm); |
|
301 |
|
302 PP("get_full_name returns %ls", fullname); |
|
303 return fullname; |
|
304 } |
|
305 |
|
306 /* End support section */ |
|
307 |
|
308 /* This section holds GSS-API exported functions */ |
|
309 |
|
310 #define CHECK_OUTPUT(x) if (!x) return GSS_S_CALL_INACCESSIBLE_WRITE; |
|
311 #define CHECK_BUFFER(b) if (!b || !b->value) return GSS_S_CALL_INACCESSIBLE_READ; |
|
312 #define CHECK_OID(o) if (!o || !o->elements) return GSS_S_CALL_INACCESSIBLE_READ; |
|
313 #define CHECK_NAME(n) if (!n || !(n->name)) return GSS_S_BAD_NAME; |
|
314 #define CHECK_CONTEXT(c) if (!c) return GSS_S_NO_CONTEXT; |
|
315 #define CHECK_CRED(c) if (!c || (!(cred_handle->phCredK) && !(cred_handle->phCredS))) \ |
|
316 return GSS_S_NO_CRED; |
|
317 |
|
318 __declspec(dllexport) OM_uint32 |
|
319 gss_release_name(OM_uint32 *minor_status, |
|
320 gss_name_t *name) |
|
321 { |
|
322 PP(">>>> Calling gss_release_name %p...", *name); |
|
323 if (name != NULL && *name != GSS_C_NO_NAME) { |
|
324 if ((*name)->name != NULL) { |
|
325 delete[] (*name)->name; |
|
326 } |
|
327 delete *name; |
|
328 *name = GSS_C_NO_NAME; |
|
329 } |
|
330 return GSS_S_COMPLETE; |
|
331 } |
|
332 |
|
333 __declspec(dllexport) OM_uint32 |
|
334 gss_import_name(OM_uint32 *minor_status, |
|
335 const gss_buffer_t input_name_buffer, |
|
336 const gss_OID input_name_type, |
|
337 gss_name_t *output_name) |
|
338 { |
|
339 PP(">>>> Calling gss_import_name..."); |
|
340 CHECK_BUFFER(input_name_buffer) |
|
341 CHECK_OUTPUT(output_name) |
|
342 |
|
343 int len = (int)input_name_buffer->length; |
|
344 LPSTR input = (LPSTR)input_name_buffer->value; |
|
345 if (input_name_type != NULL |
|
346 && is_same_oid(input_name_type, &EXPORT_NAME_OID)) { |
|
347 int mechLen = (int)input[3]; /* including 06 len */ |
|
348 len -= mechLen + 8; /* 4 header bytes, and an int32 length after OID */ |
|
349 if (len <= 0) { |
|
350 return GSS_S_FAILURE; |
|
351 } |
|
352 // Reject if mech is not krb5 |
|
353 if (mechLen - 2!= KRB5_OID.length || |
|
354 memcmp(input + 6, KRB5_OID.elements, mechLen - 2)) { |
|
355 return GSS_S_FAILURE;; |
|
356 } |
|
357 input = input + mechLen + 8; |
|
358 } |
|
359 |
|
360 SEC_WCHAR* value = new SEC_WCHAR[len + 1]; |
|
361 if (value == NULL) { |
|
362 goto err; |
|
363 } |
|
364 |
|
365 len = MultiByteToWideChar(CP_UTF8, 0, input, len, value, len+1); |
|
366 if (len == 0) { |
|
367 goto err; |
|
368 } |
|
369 value[len] = 0; |
|
370 |
|
371 PP("import_name from %ls", value); |
|
372 |
|
373 if (len > 33 && !wcscmp(value+len-33, L"@WELLKNOWN:ORG.H5L.REFERALS-REALM")) { |
|
374 // Remove the wellknown referrals realms |
|
375 value[len-33] = 0; |
|
376 len -= 33; |
|
377 } else if (value[len-1] == L'@') { |
|
378 // Remove the empty realm. It might come from an NT_EXPORT_NAME. |
|
379 value[len-1] = 0; |
|
380 len--; |
|
381 } |
|
382 if (len == 0) { |
|
383 goto err; |
|
384 } |
|
385 |
|
386 if (input_name_type != NULL |
|
387 && is_same_oid(input_name_type, &HOST_SERVICE_NAME_OID)) { |
|
388 // HOST_SERVICE_NAME_OID takes the form of service@host. |
|
389 for (int i = 0; i < len; i++) { |
|
390 if (value[i] == L'\\') { |
|
391 i++; |
|
392 continue; |
|
393 } |
|
394 if (value[i] == L'@') { |
|
395 value[i] = L'/'; |
|
396 break; |
|
397 } |
|
398 } |
|
399 PP("Host-based service now %ls", value); |
|
400 } |
|
401 PP("import_name to %ls", value); |
|
402 gss_name_struct* name = new gss_name_struct; |
|
403 if (name == NULL) { |
|
404 goto err; |
|
405 } |
|
406 name->name = value; |
|
407 *output_name = (gss_name_t) name; |
|
408 return GSS_S_COMPLETE; |
|
409 err: |
|
410 if (value != NULL) { |
|
411 delete[] value; |
|
412 } |
|
413 return GSS_S_FAILURE; |
|
414 } |
|
415 |
|
416 __declspec(dllexport) OM_uint32 |
|
417 gss_compare_name(OM_uint32 *minor_status, |
|
418 gss_const_name_t name1, |
|
419 gss_const_name_t name2, |
|
420 int *name_equal) |
|
421 { |
|
422 PP(">>>> Calling gss_compare_name..."); |
|
423 CHECK_NAME(name1) |
|
424 CHECK_NAME(name2) |
|
425 CHECK_OUTPUT(name_equal) |
|
426 |
|
427 *name_equal = 0; |
|
428 |
|
429 SEC_WCHAR* n1 = name1->name; |
|
430 SEC_WCHAR* n2 = name2->name; |
|
431 PP("Comparing %ls and %ls", n1, n2); |
|
432 int l1 = lstrlen(n1); |
|
433 int l2 = lstrlen(n2); |
|
434 if (l1 < l2 && n2[l1] != L'@' |
|
435 || l2 < l1 && n1[l2] != L'@') { |
|
436 return GSS_S_COMPLETE; // different |
|
437 } |
|
438 if (l1 > l2) { |
|
439 l1 = l2; // choose the smaller one. longer=smaller @ ... |
|
440 } |
|
441 if (CompareStringEx(LOCALE_NAME_SYSTEM_DEFAULT, NORM_IGNORECASE, |
|
442 n1, l1, n2, l1, NULL, NULL, 0) == CSTR_EQUAL) { |
|
443 *name_equal = 1; |
|
444 } |
|
445 return GSS_S_COMPLETE; |
|
446 } |
|
447 |
|
448 __declspec(dllexport) OM_uint32 |
|
449 gss_canonicalize_name(OM_uint32 *minor_status, |
|
450 gss_const_name_t input_name, |
|
451 const gss_OID mech_type, |
|
452 gss_name_t *output_name) |
|
453 { |
|
454 PP(">>>> Calling gss_canonicalize_name..."); |
|
455 CHECK_NAME(input_name) |
|
456 CHECK_OID(mech_type) |
|
457 CHECK_OUTPUT(output_name) |
|
458 |
|
459 gss_name_t names2 = new gss_name_struct; |
|
460 if (names2 == NULL) { |
|
461 return GSS_S_FAILURE; |
|
462 } |
|
463 names2->name = get_full_name(input_name->name); |
|
464 if (names2->name == NULL) { |
|
465 delete names2; |
|
466 return GSS_S_FAILURE; |
|
467 } |
|
468 *output_name = names2; |
|
469 return GSS_S_COMPLETE; |
|
470 } |
|
471 |
|
472 __declspec(dllexport) OM_uint32 |
|
473 gss_export_name(OM_uint32 *minor_status, |
|
474 gss_const_name_t input_name, |
|
475 gss_buffer_t exported_name) |
|
476 { |
|
477 PP(">>>> Calling gss_export_name..."); |
|
478 CHECK_NAME(input_name) |
|
479 CHECK_OUTPUT(exported_name) |
|
480 |
|
481 OM_uint32 result = GSS_S_FAILURE; |
|
482 SEC_WCHAR* name = input_name->name; |
|
483 SEC_WCHAR* fullname = get_full_name(name); |
|
484 if (!fullname) { |
|
485 goto err; |
|
486 } |
|
487 PP("Make fullname: %ls -> %ls", name, fullname); |
|
488 int len; |
|
489 size_t namelen = wcslen(fullname); |
|
490 if (namelen > 255) { |
|
491 goto err; |
|
492 } |
|
493 len = (int)namelen; |
|
494 // We only deal with not-so-long names. |
|
495 // 04 01 00 ** 06 ** OID len:int32 name |
|
496 int mechLen = KRB5_OID.length; |
|
497 char* buffer = new char[10 + mechLen + len]; |
|
498 if (buffer == NULL) { |
|
499 goto err; |
|
500 } |
|
501 buffer[0] = 4; |
|
502 buffer[1] = 1; |
|
503 buffer[2] = 0; |
|
504 buffer[3] = 2 + mechLen; |
|
505 buffer[4] = 6; |
|
506 buffer[5] = mechLen; |
|
507 memcpy_s(buffer + 6, mechLen, KRB5_OID.elements, mechLen); |
|
508 buffer[6 + mechLen] = buffer[7 + mechLen] = buffer[8 + mechLen] = 0; |
|
509 buffer[9 + mechLen] = (char)len; |
|
510 len = WideCharToMultiByte(CP_UTF8, 0, fullname, len, |
|
511 buffer+10+mechLen, len, NULL, NULL); |
|
512 if (len == 0) { |
|
513 delete[] buffer; |
|
514 goto err; |
|
515 } |
|
516 exported_name->length = 10 + mechLen + len; |
|
517 exported_name->value = buffer; |
|
518 result = GSS_S_COMPLETE; |
|
519 err: |
|
520 if (fullname != name) { |
|
521 delete[] fullname; |
|
522 } |
|
523 return result; |
|
524 } |
|
525 |
|
526 __declspec(dllexport) OM_uint32 |
|
527 gss_display_name(OM_uint32 *minor_status, |
|
528 gss_const_name_t input_name, |
|
529 gss_buffer_t output_name_buffer, |
|
530 gss_OID *output_name_type) |
|
531 { |
|
532 PP(">>>> Calling gss_display_name..."); |
|
533 CHECK_NAME(input_name) |
|
534 CHECK_OUTPUT(output_name_buffer) |
|
535 |
|
536 SEC_WCHAR* names = input_name->name; |
|
537 int len = (int)wcslen(names); |
|
538 char* buffer = new char[4*len+1]; |
|
539 if (buffer == NULL) { |
|
540 return GSS_S_FAILURE; |
|
541 } |
|
542 len = WideCharToMultiByte(CP_UTF8, 0, names, len, buffer, 4*len, NULL, NULL); |
|
543 if (len == 0) { |
|
544 delete[] buffer; |
|
545 return GSS_S_FAILURE; |
|
546 } |
|
547 buffer[len] = 0; |
|
548 output_name_buffer->length = len; |
|
549 output_name_buffer->value = buffer; |
|
550 PP("Name found: %ls -> %d [%s]", names, len, buffer); |
|
551 if (output_name_type != NULL) { |
|
552 *output_name_type = &KRB5_NAME_OID; |
|
553 } |
|
554 return GSS_S_COMPLETE; |
|
555 } |
|
556 |
|
557 __declspec(dllexport) OM_uint32 |
|
558 gss_acquire_cred(OM_uint32 *minor_status, |
|
559 gss_const_name_t desired_name, |
|
560 OM_uint32 time_req, |
|
561 const gss_OID_set desired_mechs, |
|
562 gss_cred_usage_t cred_usage, |
|
563 gss_cred_id_t *output_cred_handle, |
|
564 gss_OID_set *actual_mechs, |
|
565 OM_uint32 *time_rec) |
|
566 { |
|
567 PP(">>>> Calling gss_acquire_cred..."); |
|
568 CHECK_OUTPUT(output_cred_handle) |
|
569 |
|
570 SECURITY_STATUS ss; |
|
571 TimeStamp ts; |
|
572 ts.QuadPart = 0; |
|
573 cred_usage = 0; |
|
574 PP("AcquireCredentialsHandle with %d %p", cred_usage, desired_mechs); |
|
575 get_oid_set_desc(desired_mechs); |
|
576 |
|
577 BOOLEAN reqKerberos, reqSPNEGO; |
|
578 |
|
579 if (!desired_mechs) { |
|
580 reqKerberos = reqSPNEGO = TRUE; |
|
581 } else { |
|
582 if (has_oid(desired_mechs, &KRB5_OID)) { |
|
583 PP("reqKerberos"); |
|
584 reqKerberos = TRUE; |
|
585 } |
|
586 if (has_oid(desired_mechs, &SPNEGO_OID)) { |
|
587 PP("reqSPNEGO"); |
|
588 reqSPNEGO = TRUE; |
|
589 } |
|
590 if (!reqSPNEGO && !reqKerberos) { |
|
591 return GSS_S_BAD_MECH; |
|
592 } |
|
593 } |
|
594 |
|
595 if (actual_mechs) { |
|
596 *actual_mechs = GSS_C_NO_OID_SET; |
|
597 } |
|
598 |
|
599 gss_cred_id_t cred = new_cred(); |
|
600 if (cred == NULL) { |
|
601 goto err; |
|
602 } |
|
603 |
|
604 if (reqKerberos) { |
|
605 cred->phCredK = new CredHandle; |
|
606 if (cred->phCredK == NULL) { |
|
607 goto err; |
|
608 } |
|
609 ss = AcquireCredentialsHandle( |
|
610 NULL, |
|
611 L"Kerberos", |
|
612 cred_usage == 0 ? SECPKG_CRED_BOTH : |
|
613 (cred_usage == 1 ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND), |
|
614 NULL, |
|
615 NULL, |
|
616 NULL, |
|
617 NULL, |
|
618 cred->phCredK, |
|
619 &ts); |
|
620 if (!(SEC_SUCCESS(ss))) { |
|
621 delete cred->phCredK; |
|
622 cred->phCredK = NULL; |
|
623 goto err; |
|
624 } |
|
625 } |
|
626 |
|
627 if (reqSPNEGO) { |
|
628 cred->phCredS = new CredHandle; |
|
629 if (cred->phCredS == NULL) { |
|
630 goto err; |
|
631 } |
|
632 SEC_WINNT_AUTH_IDENTITY_EX auth; |
|
633 ZeroMemory(&auth, sizeof(auth)); |
|
634 auth.Version = SEC_WINNT_AUTH_IDENTITY_VERSION; |
|
635 auth.Length = sizeof(auth); |
|
636 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; |
|
637 auth.PackageList = (unsigned short*)L"Kerberos"; |
|
638 auth.PackageListLength = 8; |
|
639 ss = AcquireCredentialsHandle( |
|
640 NULL, |
|
641 L"Negotiate", |
|
642 cred_usage == 0 ? SECPKG_CRED_BOTH : |
|
643 (cred_usage == 1 ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND), |
|
644 NULL, |
|
645 &auth, |
|
646 NULL, |
|
647 NULL, |
|
648 cred->phCredS, |
|
649 &ts); |
|
650 if (!(SEC_SUCCESS(ss))) { |
|
651 delete cred->phCredS; |
|
652 cred->phCredS = NULL; |
|
653 goto err; |
|
654 } |
|
655 } |
|
656 |
|
657 if (actual_mechs) { |
|
658 if (gss_create_empty_oid_set(minor_status, actual_mechs)) { |
|
659 goto err; |
|
660 } |
|
661 if (reqKerberos) { |
|
662 if (gss_add_oid_set_member(minor_status, &KRB5_OID, actual_mechs)) { |
|
663 goto err; |
|
664 } |
|
665 } |
|
666 if (reqSPNEGO) { |
|
667 if (gss_add_oid_set_member(minor_status, &SPNEGO_OID, actual_mechs)) { |
|
668 goto err; |
|
669 } |
|
670 } |
|
671 } |
|
672 |
|
673 *output_cred_handle = (gss_cred_id_t)cred; |
|
674 |
|
675 // Note: ts here is weirdly huge, maybe because LSA retains the |
|
676 // password and can re-acquire a TGT at anytime. It will be |
|
677 // GSSCredential.INDEFINITE_LIFETIME. |
|
678 show_time("cred expiration", &ts); |
|
679 cred->time = seconds_until(1, &ts); |
|
680 if (time_rec != NULL) { |
|
681 *time_rec = cred->time; |
|
682 } |
|
683 |
|
684 // Since only default cred is supported, if there is a desired_name, |
|
685 // we must make sure it is the same as the realname of the default cred. |
|
686 if (desired_name != NULL) { |
|
687 PP("Acquiring cred with a name. Check if it's me."); |
|
688 gss_name_t realname; |
|
689 if (gss_inquire_cred(minor_status, *output_cred_handle, &realname, |
|
690 NULL, NULL, NULL) != GSS_S_COMPLETE) { |
|
691 PP("Cannot get owner name of default creds"); |
|
692 goto err; |
|
693 } |
|
694 SEC_WCHAR* rnames = realname->name; |
|
695 SEC_WCHAR* dnames = desired_name->name; |
|
696 int equals = 0; |
|
697 gss_compare_name(minor_status, realname, desired_name, &equals); |
|
698 gss_release_name(minor_status, &realname); |
|
699 PP("Comparing result: %d", equals); |
|
700 if (!equals) { |
|
701 goto err; |
|
702 } |
|
703 } |
|
704 |
|
705 return GSS_S_COMPLETE; |
|
706 err: |
|
707 if (cred) { |
|
708 OM_uint32 dummy; |
|
709 gss_release_cred(&dummy, &cred); |
|
710 } |
|
711 if (actual_mechs) { |
|
712 OM_uint32 dummy; |
|
713 gss_release_oid_set(&dummy, actual_mechs); |
|
714 } |
|
715 return GSS_S_FAILURE; |
|
716 } |
|
717 |
|
718 __declspec(dllexport) OM_uint32 |
|
719 gss_release_cred(OM_uint32 *minor_status, |
|
720 gss_cred_id_t *cred_handle) |
|
721 { |
|
722 PP(">>>> Calling gss_release_cred..."); |
|
723 if (cred_handle && *cred_handle) { |
|
724 if ((*cred_handle)->phCredK) { |
|
725 FreeCredentialsHandle((*cred_handle)->phCredK); |
|
726 delete (*cred_handle)->phCredK; |
|
727 } |
|
728 if ((*cred_handle)->phCredS) { |
|
729 FreeCredentialsHandle((*cred_handle)->phCredS); |
|
730 delete (*cred_handle)->phCredS; |
|
731 } |
|
732 delete *cred_handle; |
|
733 *cred_handle = GSS_C_NO_CREDENTIAL; |
|
734 } |
|
735 return GSS_S_COMPLETE; |
|
736 } |
|
737 |
|
738 __declspec(dllexport) OM_uint32 |
|
739 gss_inquire_cred(OM_uint32 *minor_status, |
|
740 gss_const_cred_id_t cred_handle, |
|
741 gss_name_t *name, |
|
742 OM_uint32 *lifetime, |
|
743 gss_cred_usage_t *cred_usage, |
|
744 gss_OID_set *mechanisms) |
|
745 { |
|
746 PP(">>>> Calling gss_inquire_cred..."); |
|
747 CHECK_CRED(cred_handle) |
|
748 |
|
749 CredHandle* cred = cred_handle->phCredK |
|
750 ? cred_handle->phCredK |
|
751 : cred_handle->phCredS; |
|
752 SECURITY_STATUS ss; |
|
753 if (name) { |
|
754 *name = GSS_C_NO_NAME; |
|
755 SecPkgCredentials_Names snames; |
|
756 ss = QueryCredentialsAttributes(cred, SECPKG_CRED_ATTR_NAMES, &snames); |
|
757 if (!SEC_SUCCESS(ss)) { |
|
758 return GSS_S_FAILURE; |
|
759 } |
|
760 SEC_WCHAR* names = new SEC_WCHAR[lstrlen(snames.sUserName) + 1]; |
|
761 if (names == NULL) { |
|
762 return GSS_S_FAILURE; |
|
763 } |
|
764 StringCchCopy(names, lstrlen(snames.sUserName) + 1, snames.sUserName); |
|
765 FreeContextBuffer(snames.sUserName); |
|
766 PP("Allocate new name at %p", names); |
|
767 gss_name_t name1 = new gss_name_struct; |
|
768 if (name1 == NULL) { |
|
769 delete[] names; |
|
770 return GSS_S_FAILURE; |
|
771 } |
|
772 name1->name = names; |
|
773 *name = (gss_name_t) name1; |
|
774 } |
|
775 if (lifetime) { |
|
776 *lifetime = cred_handle->time; |
|
777 } |
|
778 if (cred_usage) { |
|
779 *cred_usage = 1; // We only support INITIATE_ONLY now |
|
780 } |
|
781 if (mechanisms) { |
|
782 // Useless for Java |
|
783 } |
|
784 // Others inquiries not supported yet |
|
785 return GSS_S_COMPLETE; |
|
786 } |
|
787 |
|
788 __declspec(dllexport) OM_uint32 |
|
789 gss_import_sec_context(OM_uint32 *minor_status, |
|
790 const gss_buffer_t interprocess_token, |
|
791 gss_ctx_id_t *context_handle) |
|
792 { |
|
793 // Not transferable, return FAILURE |
|
794 PP(">>>> Calling UNIMPLEMENTED gss_import_sec_context..."); |
|
795 *minor_status = 0; |
|
796 return GSS_S_FAILURE; |
|
797 } |
|
798 |
|
799 __declspec(dllexport) OM_uint32 |
|
800 gss_init_sec_context(OM_uint32 *minor_status, |
|
801 gss_const_cred_id_t initiator_cred_handle, |
|
802 gss_ctx_id_t *context_handle, |
|
803 gss_const_name_t target_name, |
|
804 const gss_OID mech_type, |
|
805 OM_uint32 req_flags, |
|
806 OM_uint32 time_req, |
|
807 const gss_channel_bindings_t input_chan_bindings, |
|
808 const gss_buffer_t input_token, |
|
809 gss_OID *actual_mech_type, |
|
810 gss_buffer_t output_token, |
|
811 OM_uint32 *ret_flags, |
|
812 OM_uint32 *time_rec) |
|
813 { |
|
814 PP(">>>> Calling gss_init_sec_context..."); |
|
815 CHECK_NAME(target_name) |
|
816 CHECK_OUTPUT(output_token) |
|
817 |
|
818 SECURITY_STATUS ss; |
|
819 TimeStamp lifeTime; |
|
820 SecBufferDesc inBuffDesc; |
|
821 SecBuffer inSecBuff; |
|
822 SecBufferDesc outBuffDesc; |
|
823 SecBuffer outSecBuff; |
|
824 BOOLEAN isSPNEGO = is_same_oid(mech_type, &SPNEGO_OID); |
|
825 |
|
826 gss_ctx_id_t pc; |
|
827 |
|
828 output_token->length = 0; |
|
829 output_token->value = NULL; |
|
830 |
|
831 BOOLEAN firstTime = (*context_handle == GSS_C_NO_CONTEXT); |
|
832 PP("First time? %d", firstTime); |
|
833 if (firstTime) { |
|
834 pc = new_context(isSPNEGO); |
|
835 if (pc == NULL) { |
|
836 return GSS_S_FAILURE; |
|
837 } |
|
838 *context_handle = (gss_ctx_id_t) pc; |
|
839 } else { |
|
840 pc = *context_handle; |
|
841 } |
|
842 |
|
843 if (pc == NULL) { |
|
844 return GSS_S_NO_CONTEXT; |
|
845 } |
|
846 |
|
847 DWORD outFlag; |
|
848 TCHAR outName[100]; |
|
849 |
|
850 OM_uint32 minor; |
|
851 gss_buffer_desc tn; |
|
852 gss_display_name(&minor, target_name, &tn, NULL); |
|
853 int len = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)tn.value, (int)tn.length, |
|
854 outName, sizeof(outName) - 1); |
|
855 if (len == 0) { |
|
856 goto err; |
|
857 } |
|
858 outName[len] = 0; |
|
859 |
|
860 int flag = flag_gss_to_sspi(req_flags) | ISC_REQ_ALLOCATE_MEMORY; |
|
861 |
|
862 outBuffDesc.ulVersion = SECBUFFER_VERSION; |
|
863 outBuffDesc.cBuffers = 1; |
|
864 outBuffDesc.pBuffers = &outSecBuff; |
|
865 |
|
866 outSecBuff.BufferType = SECBUFFER_TOKEN; |
|
867 |
|
868 if (!firstTime) { |
|
869 inBuffDesc.ulVersion = SECBUFFER_VERSION; |
|
870 inBuffDesc.cBuffers = 1; |
|
871 inBuffDesc.pBuffers = &inSecBuff; |
|
872 |
|
873 inSecBuff.BufferType = SECBUFFER_TOKEN; |
|
874 inSecBuff.cbBuffer = (ULONG)input_token->length; |
|
875 inSecBuff.pvBuffer = input_token->value; |
|
876 } else if (!pc->phCred) { |
|
877 if (isSPNEGO && initiator_cred_handle |
|
878 && initiator_cred_handle->phCredS) { |
|
879 PP("Find SPNEGO credentials"); |
|
880 pc->phCred = initiator_cred_handle->phCredS; |
|
881 pc->isLocalCred = FALSE; |
|
882 } else if (!isSPNEGO && initiator_cred_handle |
|
883 && initiator_cred_handle->phCredK) { |
|
884 PP("Find Kerberos credentials"); |
|
885 pc->phCred = initiator_cred_handle->phCredK; |
|
886 pc->isLocalCred = FALSE; |
|
887 } else { |
|
888 PP("No credentials provided, acquire myself"); |
|
889 CredHandle* newCred = new CredHandle; |
|
890 SEC_WINNT_AUTH_IDENTITY_EX auth; |
|
891 ZeroMemory(&auth, sizeof(auth)); |
|
892 auth.Version = SEC_WINNT_AUTH_IDENTITY_VERSION; |
|
893 auth.Length = sizeof(auth); |
|
894 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; |
|
895 auth.PackageList = (unsigned short*)L"Kerberos"; |
|
896 auth.PackageListLength = 8; |
|
897 ss = AcquireCredentialsHandle( |
|
898 NULL, |
|
899 isSPNEGO ? L"Negotiate" : L"Kerberos", |
|
900 SECPKG_CRED_OUTBOUND, |
|
901 NULL, |
|
902 isSPNEGO ? &auth : NULL, |
|
903 NULL, |
|
904 NULL, |
|
905 newCred, |
|
906 &lifeTime); |
|
907 if (!(SEC_SUCCESS(ss))) { |
|
908 delete newCred; |
|
909 goto err; |
|
910 } |
|
911 pc->phCred = newCred; |
|
912 pc->isLocalCred = TRUE; |
|
913 } |
|
914 } |
|
915 ss = InitializeSecurityContext( |
|
916 pc->phCred, |
|
917 firstTime ? NULL : &pc->hCtxt, |
|
918 outName, |
|
919 flag, |
|
920 0, |
|
921 SECURITY_NATIVE_DREP, |
|
922 firstTime ? NULL : &inBuffDesc, |
|
923 0, |
|
924 &pc->hCtxt, |
|
925 &outBuffDesc, |
|
926 &outFlag, |
|
927 &lifeTime); |
|
928 |
|
929 if (!SEC_SUCCESS(ss)) { |
|
930 // TODO: seems NativeGSSContext has not failed here. |
|
931 PP("InitializeSecurityContext failed"); |
|
932 goto err; |
|
933 } |
|
934 |
|
935 pc->flags = *ret_flags = flag_sspi_to_gss(outFlag); |
|
936 |
|
937 // Ignore the result of the next call. Might fail before context established. |
|
938 QueryContextAttributes( |
|
939 &pc->hCtxt, SECPKG_ATTR_SIZES, &pc->SecPkgContextSizes); |
|
940 PP("cbMaxSignature: %ld. cbBlockSize: %ld. cbSecurityTrailer: %ld", |
|
941 pc->SecPkgContextSizes.cbMaxSignature, |
|
942 pc->SecPkgContextSizes.cbBlockSize, |
|
943 pc->SecPkgContextSizes.cbSecurityTrailer); |
|
944 |
|
945 output_token->length = outSecBuff.cbBuffer; |
|
946 if (outSecBuff.cbBuffer) { |
|
947 // No idea how user would free the data. Let's duplicate one. |
|
948 output_token->value = new char[outSecBuff.cbBuffer]; |
|
949 if (!output_token->value) { |
|
950 FreeContextBuffer(outSecBuff.pvBuffer); |
|
951 output_token->length = 0; |
|
952 goto err; |
|
953 } |
|
954 memcpy(output_token->value, outSecBuff.pvBuffer, outSecBuff.cbBuffer); |
|
955 FreeContextBuffer(outSecBuff.pvBuffer); |
|
956 } |
|
957 |
|
958 if (ss == SEC_I_CONTINUE_NEEDED) { |
|
959 return GSS_S_CONTINUE_NEEDED; |
|
960 } else { |
|
961 pc->established = true; |
|
962 ss = QueryContextAttributes(&pc->hCtxt, SECPKG_ATTR_NATIVE_NAMES, &pc->nnames); |
|
963 if (!SEC_SUCCESS(ss)) { |
|
964 goto err; |
|
965 } |
|
966 PP("Names. %ls %ls", pc->nnames.sClientName, pc->nnames.sServerName); |
|
967 *ret_flags |= GSS_C_PROT_READY_FLAG; |
|
968 return GSS_S_COMPLETE; |
|
969 } |
|
970 err: |
|
971 if (firstTime) { |
|
972 OM_uint32 dummy; |
|
973 gss_delete_sec_context(&dummy, context_handle, GSS_C_NO_BUFFER); |
|
974 } |
|
975 if (output_token->value) { |
|
976 gss_release_buffer(NULL, output_token); |
|
977 output_token = GSS_C_NO_BUFFER; |
|
978 } |
|
979 return GSS_S_FAILURE; |
|
980 } |
|
981 |
|
982 __declspec(dllexport) OM_uint32 |
|
983 gss_accept_sec_context(OM_uint32 *minor_status, |
|
984 gss_ctx_id_t *context_handle, |
|
985 gss_const_cred_id_t acceptor_cred_handle, |
|
986 const gss_buffer_t input_token, |
|
987 const gss_channel_bindings_t input_chan_bindings, |
|
988 gss_name_t *src_name, |
|
989 gss_OID *mech_type, |
|
990 gss_buffer_t output_token, |
|
991 OM_uint32 *ret_flags, |
|
992 OM_uint32 *time_rec, |
|
993 gss_cred_id_t *delegated_cred_handle) |
|
994 { |
|
995 PP(">>>> Calling UNIMPLEMENTED gss_accept_sec_context..."); |
|
996 PP("gss_accept_sec_context is not supported in this initiator-only library"); |
|
997 return GSS_S_FAILURE; |
|
998 } |
|
999 |
|
1000 __declspec(dllexport) OM_uint32 |
|
1001 gss_inquire_context(OM_uint32 *minor_status, |
|
1002 gss_const_ctx_id_t context_handle, |
|
1003 gss_name_t *src_name, |
|
1004 gss_name_t *targ_name, |
|
1005 OM_uint32 *lifetime_rec, |
|
1006 gss_OID *mech_type, |
|
1007 OM_uint32 *ctx_flags, |
|
1008 int *locally_initiated, |
|
1009 int *open) |
|
1010 { |
|
1011 PP(">>>> Calling gss_inquire_context..."); |
|
1012 CHECK_CONTEXT(context_handle) |
|
1013 |
|
1014 gss_name_t n1 = NULL; |
|
1015 gss_name_t n2 = NULL; |
|
1016 if (!context_handle->established) { |
|
1017 return GSS_S_NO_CONTEXT; |
|
1018 } |
|
1019 if (src_name != NULL) { |
|
1020 n1 = new gss_name_struct; |
|
1021 if (n1 == NULL) { |
|
1022 goto err; |
|
1023 } |
|
1024 n1->name = new SEC_WCHAR[lstrlen(context_handle->nnames.sClientName) + 1]; |
|
1025 if (n1->name == NULL) { |
|
1026 goto err; |
|
1027 } |
|
1028 PP("Allocate new name at %p", n1->name); |
|
1029 StringCchCopy(n1->name, lstrlen(context_handle->nnames.sClientName) + 1, |
|
1030 context_handle->nnames.sClientName); |
|
1031 *src_name = (gss_name_t) n1; |
|
1032 } |
|
1033 if (targ_name != NULL) { |
|
1034 n2 = new gss_name_struct; |
|
1035 if (n2 == NULL) { |
|
1036 goto err; |
|
1037 } |
|
1038 n2->name = new SEC_WCHAR[lstrlen(context_handle->nnames.sServerName) + 1]; |
|
1039 if (n2->name == NULL) { |
|
1040 goto err; |
|
1041 } |
|
1042 PP("Allocate new name at %p", n2->name); |
|
1043 StringCchCopy(n2->name, lstrlen(context_handle->nnames.sServerName) + 1, |
|
1044 context_handle->nnames.sServerName); |
|
1045 *targ_name = (gss_name_t) n2; |
|
1046 } |
|
1047 if (lifetime_rec != NULL) { |
|
1048 SecPkgContext_Lifespan ls; |
|
1049 SECURITY_STATUS ss; |
|
1050 ss = QueryContextAttributes( |
|
1051 (PCtxtHandle)&context_handle->hCtxt, |
|
1052 SECPKG_ATTR_LIFESPAN, |
|
1053 &ls); |
|
1054 if (!SEC_SUCCESS(ss)) { |
|
1055 goto err; |
|
1056 } |
|
1057 *lifetime_rec = seconds_until(0, &ls.tsExpiry); |
|
1058 } |
|
1059 if (mech_type != NULL) { |
|
1060 *mech_type = context_handle->isSPNEGO |
|
1061 ? &SPNEGO_OID : &KRB5_OID; |
|
1062 } |
|
1063 if (ctx_flags != NULL) { |
|
1064 *ctx_flags = context_handle->flags; |
|
1065 } |
|
1066 if (locally_initiated != NULL) { |
|
1067 // We are always initiator |
|
1068 *locally_initiated = 1; |
|
1069 } |
|
1070 return GSS_S_COMPLETE; |
|
1071 err: |
|
1072 if (n1 != NULL) { |
|
1073 if (n1->name != NULL) { |
|
1074 delete[] n1->name; |
|
1075 } |
|
1076 delete n1; |
|
1077 n1 = NULL; |
|
1078 } |
|
1079 if (n2 != NULL) { |
|
1080 if (n2->name != NULL) { |
|
1081 delete[] n2->name; |
|
1082 } |
|
1083 delete n2; |
|
1084 n2 = NULL; |
|
1085 } |
|
1086 return GSS_S_FAILURE; |
|
1087 } |
|
1088 |
|
1089 __declspec(dllexport) OM_uint32 |
|
1090 gss_delete_sec_context(OM_uint32 *minor_status, |
|
1091 gss_ctx_id_t *context_handle, |
|
1092 gss_buffer_t output_token) |
|
1093 { |
|
1094 PP(">>>> Calling gss_delete_sec_context..."); |
|
1095 CHECK_CONTEXT(context_handle) |
|
1096 |
|
1097 DeleteSecurityContext(&(*context_handle)->hCtxt); |
|
1098 if ((*context_handle)->isLocalCred && (*context_handle)->phCred != NULL) { |
|
1099 FreeCredentialsHandle((*context_handle)->phCred); |
|
1100 (*context_handle)->phCred = NULL; |
|
1101 } |
|
1102 if ((*context_handle)->nnames.sClientName != NULL) { |
|
1103 FreeContextBuffer((*context_handle)->nnames.sClientName); |
|
1104 (*context_handle)->nnames.sClientName = NULL; |
|
1105 } |
|
1106 if ((*context_handle)->nnames.sServerName != NULL) { |
|
1107 FreeContextBuffer((*context_handle)->nnames.sServerName); |
|
1108 (*context_handle)->nnames.sServerName = NULL; |
|
1109 } |
|
1110 delete (*context_handle); |
|
1111 *context_handle = GSS_C_NO_CONTEXT; |
|
1112 return GSS_S_COMPLETE; |
|
1113 } |
|
1114 |
|
1115 __declspec(dllexport) OM_uint32 |
|
1116 gss_context_time(OM_uint32 *minor_status, |
|
1117 gss_const_ctx_id_t context_handle, |
|
1118 OM_uint32 *time_rec) |
|
1119 { |
|
1120 PP(">>>> Calling IMPLEMENTED gss_context_time..."); |
|
1121 CHECK_CONTEXT(context_handle) |
|
1122 CHECK_OUTPUT(time_rec) |
|
1123 |
|
1124 SECURITY_STATUS ss; |
|
1125 SecPkgContext_Lifespan ls; |
|
1126 ss = QueryContextAttributes( |
|
1127 (PCtxtHandle)&context_handle->hCtxt, |
|
1128 SECPKG_ATTR_LIFESPAN, |
|
1129 &ls); |
|
1130 if (ss == SEC_E_OK) { |
|
1131 *time_rec = seconds_until(0, &ls.tsExpiry); |
|
1132 show_time("context start", &ls.tsStart); |
|
1133 show_time("context expiry", &ls.tsExpiry); |
|
1134 return *time_rec == 0 ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; |
|
1135 } else { |
|
1136 return GSS_S_FAILURE; |
|
1137 } |
|
1138 } |
|
1139 |
|
1140 __declspec(dllexport) OM_uint32 |
|
1141 gss_wrap_size_limit(OM_uint32 *minor_status, |
|
1142 gss_const_ctx_id_t context_handle, |
|
1143 int conf_req_flag, |
|
1144 gss_qop_t qop_req, |
|
1145 OM_uint32 req_output_size, |
|
1146 OM_uint32 *max_input_size) |
|
1147 { |
|
1148 PP(">>>> Calling gss_wrap_size_limit..."); |
|
1149 CHECK_CONTEXT(context_handle) |
|
1150 CHECK_OUTPUT(max_input_size) |
|
1151 |
|
1152 *max_input_size = req_output_size |
|
1153 - context_handle->SecPkgContextSizes.cbSecurityTrailer |
|
1154 - context_handle->SecPkgContextSizes.cbBlockSize; |
|
1155 return GSS_S_COMPLETE; |
|
1156 } |
|
1157 |
|
1158 __declspec(dllexport) OM_uint32 |
|
1159 gss_export_sec_context(OM_uint32 *minor_status, |
|
1160 gss_ctx_id_t *context_handle, |
|
1161 gss_buffer_t interprocess_token) |
|
1162 { |
|
1163 PP(">>>> Calling UNIMPLEMENTED gss_export_sec_context..."); |
|
1164 return GSS_S_FAILURE; |
|
1165 } |
|
1166 |
|
1167 __declspec(dllexport) OM_uint32 |
|
1168 gss_get_mic(OM_uint32 *minor_status, |
|
1169 gss_const_ctx_id_t context_handle, |
|
1170 gss_qop_t qop_req, |
|
1171 const gss_buffer_t message_buffer, |
|
1172 gss_buffer_t msg_token) |
|
1173 { |
|
1174 PP(">>>> Calling gss_get_mic..."); |
|
1175 CHECK_CONTEXT(context_handle); |
|
1176 CHECK_BUFFER(message_buffer); |
|
1177 CHECK_OUTPUT(msg_token); |
|
1178 |
|
1179 SECURITY_STATUS ss; |
|
1180 SecBufferDesc buffDesc; |
|
1181 SecBuffer secBuff[2]; |
|
1182 |
|
1183 buffDesc.cBuffers = 2; |
|
1184 buffDesc.pBuffers = secBuff; |
|
1185 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1186 |
|
1187 secBuff[0].BufferType = SECBUFFER_DATA; |
|
1188 secBuff[0].cbBuffer = (ULONG)message_buffer->length; |
|
1189 secBuff[0].pvBuffer = message_buffer->value; |
|
1190 |
|
1191 secBuff[1].BufferType = SECBUFFER_TOKEN; |
|
1192 secBuff[1].cbBuffer = context_handle->SecPkgContextSizes.cbMaxSignature; |
|
1193 secBuff[1].pvBuffer = msg_token->value = new char[secBuff[1].cbBuffer]; |
|
1194 |
|
1195 ss = MakeSignature((PCtxtHandle)&context_handle->hCtxt, 0, &buffDesc, 0); |
|
1196 |
|
1197 if (!SEC_SUCCESS(ss)) { |
|
1198 msg_token->length = 0; |
|
1199 msg_token->value = NULL; |
|
1200 delete[] secBuff[1].pvBuffer; |
|
1201 return GSS_S_FAILURE; |
|
1202 } |
|
1203 |
|
1204 msg_token->length = secBuff[1].cbBuffer; |
|
1205 return GSS_S_COMPLETE; |
|
1206 } |
|
1207 |
|
1208 __declspec(dllexport) OM_uint32 |
|
1209 gss_verify_mic(OM_uint32 *minor_status, |
|
1210 gss_const_ctx_id_t context_handle, |
|
1211 const gss_buffer_t message_buffer, |
|
1212 const gss_buffer_t token_buffer, |
|
1213 gss_qop_t *qop_state) |
|
1214 { |
|
1215 PP(">>>> Calling gss_verify_mic..."); |
|
1216 CHECK_CONTEXT(context_handle); |
|
1217 CHECK_BUFFER(message_buffer); |
|
1218 CHECK_BUFFER(token_buffer); |
|
1219 |
|
1220 SECURITY_STATUS ss; |
|
1221 SecBufferDesc buffDesc; |
|
1222 SecBuffer secBuff[2]; |
|
1223 ULONG qop; |
|
1224 |
|
1225 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1226 buffDesc.cBuffers = 2; |
|
1227 buffDesc.pBuffers = secBuff; |
|
1228 |
|
1229 secBuff[0].BufferType = SECBUFFER_TOKEN; |
|
1230 secBuff[0].cbBuffer = (ULONG)token_buffer->length; |
|
1231 secBuff[0].pvBuffer = token_buffer->value; |
|
1232 |
|
1233 secBuff[1].BufferType = SECBUFFER_DATA; |
|
1234 secBuff[1].cbBuffer = (ULONG)message_buffer->length; |
|
1235 secBuff[1].pvBuffer = message_buffer->value; |
|
1236 |
|
1237 ss = VerifySignature((PCtxtHandle)&context_handle->hCtxt, &buffDesc, 0, &qop); |
|
1238 if (qop_state) { |
|
1239 *qop_state = qop; |
|
1240 } |
|
1241 |
|
1242 if (ss == SEC_E_OK) { |
|
1243 return GSS_S_COMPLETE; |
|
1244 } else if (ss == SEC_E_OUT_OF_SEQUENCE) { |
|
1245 return GSS_S_UNSEQ_TOKEN; |
|
1246 } else { |
|
1247 return GSS_S_BAD_SIG; |
|
1248 } |
|
1249 } |
|
1250 |
|
1251 __declspec(dllexport) OM_uint32 |
|
1252 gss_wrap(OM_uint32 *minor_status, |
|
1253 gss_const_ctx_id_t context_handle, |
|
1254 int conf_req_flag, |
|
1255 gss_qop_t qop_req, |
|
1256 const gss_buffer_t input_message_buffer, |
|
1257 int *conf_state, |
|
1258 gss_buffer_t output_message_buffer) |
|
1259 { |
|
1260 PP(">>>> Calling gss_wrap..."); |
|
1261 CHECK_CONTEXT(context_handle); |
|
1262 CHECK_BUFFER(input_message_buffer); |
|
1263 CHECK_OUTPUT(output_message_buffer); |
|
1264 |
|
1265 SECURITY_STATUS ss; |
|
1266 SecBufferDesc buffDesc; |
|
1267 SecBuffer secBuff[3]; |
|
1268 |
|
1269 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1270 buffDesc.cBuffers = 3; |
|
1271 buffDesc.pBuffers = secBuff; |
|
1272 |
|
1273 secBuff[0].BufferType = SECBUFFER_TOKEN; |
|
1274 secBuff[0].cbBuffer = context_handle->SecPkgContextSizes.cbSecurityTrailer; |
|
1275 output_message_buffer->value = secBuff[0].pvBuffer = malloc( |
|
1276 context_handle->SecPkgContextSizes.cbSecurityTrailer |
|
1277 + input_message_buffer->length |
|
1278 + context_handle->SecPkgContextSizes.cbBlockSize);; |
|
1279 |
|
1280 secBuff[1].BufferType = SECBUFFER_DATA; |
|
1281 secBuff[1].cbBuffer = (ULONG)input_message_buffer->length; |
|
1282 secBuff[1].pvBuffer = malloc(secBuff[1].cbBuffer); |
|
1283 memcpy_s(secBuff[1].pvBuffer, secBuff[1].cbBuffer, |
|
1284 input_message_buffer->value, input_message_buffer->length); |
|
1285 |
|
1286 secBuff[2].BufferType = SECBUFFER_PADDING; |
|
1287 secBuff[2].cbBuffer = context_handle->SecPkgContextSizes.cbBlockSize; |
|
1288 secBuff[2].pvBuffer = malloc(secBuff[2].cbBuffer); |
|
1289 |
|
1290 ss = EncryptMessage((PCtxtHandle)&context_handle->hCtxt, |
|
1291 conf_req_flag ? 0 : SECQOP_WRAP_NO_ENCRYPT, |
|
1292 &buffDesc, 0); |
|
1293 if (conf_state) { |
|
1294 *conf_state = conf_req_flag; |
|
1295 } |
|
1296 |
|
1297 if (!SEC_SUCCESS(ss)) { |
|
1298 free(secBuff[0].pvBuffer); |
|
1299 free(secBuff[1].pvBuffer); |
|
1300 free(secBuff[2].pvBuffer); |
|
1301 output_message_buffer->length = 0; |
|
1302 output_message_buffer->value = NULL; |
|
1303 return GSS_S_FAILURE; |
|
1304 } |
|
1305 |
|
1306 memcpy_s((PBYTE)secBuff[0].pvBuffer + secBuff[0].cbBuffer, |
|
1307 input_message_buffer->length + context_handle->SecPkgContextSizes.cbBlockSize, |
|
1308 secBuff[1].pvBuffer, |
|
1309 secBuff[1].cbBuffer); |
|
1310 memcpy_s((PBYTE)secBuff[0].pvBuffer + secBuff[0].cbBuffer + secBuff[1].cbBuffer, |
|
1311 context_handle->SecPkgContextSizes.cbBlockSize, |
|
1312 secBuff[2].pvBuffer, |
|
1313 secBuff[2].cbBuffer); |
|
1314 |
|
1315 output_message_buffer->length = secBuff[0].cbBuffer + secBuff[1].cbBuffer |
|
1316 + secBuff[2].cbBuffer; |
|
1317 free(secBuff[1].pvBuffer); |
|
1318 free(secBuff[2].pvBuffer); |
|
1319 |
|
1320 return GSS_S_COMPLETE; |
|
1321 } |
|
1322 |
|
1323 __declspec(dllexport) OM_uint32 |
|
1324 gss_unwrap(OM_uint32 *minor_status, |
|
1325 gss_const_ctx_id_t context_handle, |
|
1326 const gss_buffer_t input_message_buffer, |
|
1327 gss_buffer_t output_message_buffer, |
|
1328 int *conf_state, |
|
1329 gss_qop_t *qop_state) |
|
1330 { |
|
1331 PP(">>>> Calling gss_unwrap..."); |
|
1332 CHECK_CONTEXT(context_handle); |
|
1333 CHECK_BUFFER(input_message_buffer); |
|
1334 CHECK_OUTPUT(output_message_buffer); |
|
1335 |
|
1336 SECURITY_STATUS ss; |
|
1337 SecBufferDesc buffDesc; |
|
1338 SecBuffer secBuff[2]; |
|
1339 ULONG ulQop = 0; |
|
1340 |
|
1341 buffDesc.cBuffers = 2; |
|
1342 buffDesc.pBuffers = secBuff; |
|
1343 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1344 |
|
1345 secBuff[0].BufferType = SECBUFFER_STREAM; |
|
1346 secBuff[0].cbBuffer = (ULONG)input_message_buffer->length; |
|
1347 secBuff[0].pvBuffer = malloc(input_message_buffer->length); |
|
1348 memcpy_s(secBuff[0].pvBuffer, input_message_buffer->length, |
|
1349 input_message_buffer->value, input_message_buffer->length); |
|
1350 |
|
1351 secBuff[1].BufferType = SECBUFFER_DATA; |
|
1352 secBuff[1].cbBuffer = 0; |
|
1353 secBuff[1].pvBuffer = NULL; |
|
1354 |
|
1355 ss = DecryptMessage((PCtxtHandle)&context_handle->hCtxt, &buffDesc, 0, &ulQop); |
|
1356 if (qop_state) { |
|
1357 *qop_state = ulQop; |
|
1358 } |
|
1359 if (!SEC_SUCCESS(ss)) { |
|
1360 free(secBuff[0].pvBuffer); |
|
1361 output_message_buffer->length = 0; |
|
1362 output_message_buffer->value = NULL; |
|
1363 return GSS_S_FAILURE; |
|
1364 } |
|
1365 |
|
1366 // Must allocate a new memory block so client can release it correctly |
|
1367 output_message_buffer->length = secBuff[1].cbBuffer; |
|
1368 output_message_buffer->value = new char[secBuff[1].cbBuffer]; |
|
1369 memcpy_s(output_message_buffer->value, secBuff[1].cbBuffer, |
|
1370 secBuff[1].pvBuffer, secBuff[1].cbBuffer); |
|
1371 *conf_state = ulQop == SECQOP_WRAP_NO_ENCRYPT ? 0 : 1; |
|
1372 |
|
1373 free(secBuff[0].pvBuffer); |
|
1374 return GSS_S_COMPLETE; |
|
1375 } |
|
1376 |
|
1377 __declspec(dllexport) OM_uint32 |
|
1378 gss_indicate_mechs(OM_uint32 *minor_status, |
|
1379 gss_OID_set *mech_set) |
|
1380 { |
|
1381 PP(">>>> Calling gss_indicate_mechs..."); |
|
1382 OM_uint32 major = GSS_S_COMPLETE; |
|
1383 |
|
1384 ULONG ccPackages; |
|
1385 PSecPkgInfo packages; |
|
1386 EnumerateSecurityPackages(&ccPackages, &packages); |
|
1387 PP("EnumerateSecurityPackages returns %ld", ccPackages); |
|
1388 for (unsigned int i = 0; i < ccPackages; i++) { |
|
1389 PP("#%d: %ls, %ls\n", i, packages[i].Name, packages[i].Comment); |
|
1390 } |
|
1391 FreeContextBuffer(packages); |
|
1392 |
|
1393 // Hardcode kerberos and SPNEGO support |
|
1394 major = gss_create_empty_oid_set(minor_status, mech_set); |
|
1395 if (major != GSS_S_COMPLETE) { |
|
1396 goto done; |
|
1397 } |
|
1398 |
|
1399 major = gss_add_oid_set_member(minor_status, &KRB5_OID, mech_set); |
|
1400 if (major != GSS_S_COMPLETE) { |
|
1401 goto done; |
|
1402 } |
|
1403 |
|
1404 major = gss_add_oid_set_member(minor_status, &SPNEGO_OID, mech_set); |
|
1405 if (major != GSS_S_COMPLETE) { |
|
1406 goto done; |
|
1407 } |
|
1408 |
|
1409 done: |
|
1410 |
|
1411 if (major != GSS_S_COMPLETE) { |
|
1412 gss_release_oid_set(minor_status, mech_set); |
|
1413 } |
|
1414 |
|
1415 return major; |
|
1416 } |
|
1417 |
|
1418 __declspec(dllexport) OM_uint32 |
|
1419 gss_inquire_names_for_mech(OM_uint32 *minor_status, |
|
1420 const gss_OID mechanism, |
|
1421 gss_OID_set *name_types) |
|
1422 { |
|
1423 PP(">>>> Calling gss_inquire_names_for_mech..."); |
|
1424 CHECK_OID(mechanism); |
|
1425 |
|
1426 if (gss_create_empty_oid_set(minor_status, name_types)) { |
|
1427 return GSS_S_FAILURE; |
|
1428 } |
|
1429 if (gss_add_oid_set_member(minor_status, &USER_NAME_OID, name_types)) { |
|
1430 goto err; |
|
1431 } |
|
1432 if (gss_add_oid_set_member(minor_status, &HOST_SERVICE_NAME_OID, name_types)) { |
|
1433 goto err; |
|
1434 } |
|
1435 if (!is_same_oid(mechanism, &SPNEGO_OID)) { |
|
1436 if (gss_add_oid_set_member(minor_status, &EXPORT_NAME_OID, name_types)) { |
|
1437 goto err; |
|
1438 } |
|
1439 } |
|
1440 return GSS_S_COMPLETE; |
|
1441 err: |
|
1442 gss_release_oid_set(minor_status, name_types); |
|
1443 return GSS_S_FAILURE; |
|
1444 } |
|
1445 |
|
1446 __declspec(dllexport) OM_uint32 |
|
1447 gss_add_oid_set_member(OM_uint32 *minor_status, |
|
1448 const gss_OID member_oid, |
|
1449 gss_OID_set *oid_set) |
|
1450 { |
|
1451 PP(">>>> Calling gss_add_oid_set_member..."); |
|
1452 CHECK_OID(member_oid); |
|
1453 CHECK_OUTPUT(oid_set); |
|
1454 |
|
1455 |
|
1456 int count = (int)(*oid_set)->count; |
|
1457 for (int i = 0; i < count; i++) { |
|
1458 if (is_same_oid(&(*oid_set)->elements[i], member_oid)) { |
|
1459 // already there |
|
1460 return GSS_S_COMPLETE; |
|
1461 } |
|
1462 } |
|
1463 gss_OID existing = (*oid_set)->elements; |
|
1464 gss_OID newcopy = new gss_OID_desc[count + 1]; |
|
1465 if (newcopy == NULL) { |
|
1466 return GSS_S_FAILURE; |
|
1467 } |
|
1468 if (existing) { |
|
1469 memcpy_s(newcopy, (count + 1) * sizeof(gss_OID_desc), |
|
1470 existing, count * sizeof(gss_OID_desc)); |
|
1471 } |
|
1472 newcopy[count].length = member_oid->length; |
|
1473 newcopy[count].elements = new char[member_oid->length]; |
|
1474 if (newcopy[count].elements == NULL) { |
|
1475 delete[] newcopy; |
|
1476 return GSS_S_FAILURE; |
|
1477 } |
|
1478 memcpy_s(newcopy[count].elements, member_oid->length, |
|
1479 member_oid->elements, member_oid->length); |
|
1480 (*oid_set)->elements = newcopy; |
|
1481 (*oid_set)->count++; |
|
1482 if (existing) { |
|
1483 delete[] existing; |
|
1484 } |
|
1485 |
|
1486 return GSS_S_COMPLETE; |
|
1487 } |
|
1488 |
|
1489 __declspec(dllexport) OM_uint32 |
|
1490 gss_display_status(OM_uint32 *minor_status, |
|
1491 OM_uint32 status_value, |
|
1492 int status_type, |
|
1493 const gss_OID mech_type, |
|
1494 OM_uint32 *message_context, |
|
1495 gss_buffer_t status_string) |
|
1496 { |
|
1497 PP(">>>> Calling gss_display_status..."); |
|
1498 TCHAR msg[256]; |
|
1499 int len = FormatMessage( |
|
1500 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
|
1501 0, status_value, |
|
1502 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|
1503 msg, 256, 0); |
|
1504 if (len > 0) { |
|
1505 status_string->value = new char[len + 20]; |
|
1506 status_string->length = sprintf_s( |
|
1507 (LPSTR)status_string->value, len + 19, |
|
1508 "(%lx) %ls", status_value, msg); |
|
1509 } else { |
|
1510 status_string->value = new char[33]; |
|
1511 status_string->length = sprintf_s( |
|
1512 (LPSTR)status_string->value, 32, |
|
1513 "status is %lx", status_value); |
|
1514 } |
|
1515 if (status_string->length <= 0) { |
|
1516 gss_release_buffer(NULL, status_string); |
|
1517 status_string = GSS_C_NO_BUFFER; |
|
1518 return GSS_S_FAILURE; |
|
1519 } else { |
|
1520 return GSS_S_COMPLETE; |
|
1521 } |
|
1522 } |
|
1523 |
|
1524 __declspec(dllexport) OM_uint32 |
|
1525 gss_create_empty_oid_set(OM_uint32 *minor_status, |
|
1526 gss_OID_set *oid_set) |
|
1527 { |
|
1528 PP(">>>> Calling gss_create_empty_oid_set..."); |
|
1529 CHECK_OUTPUT(oid_set); |
|
1530 |
|
1531 if (*oid_set = new gss_OID_set_desc) { |
|
1532 memset(*oid_set, 0, sizeof(gss_OID_set_desc)); |
|
1533 return GSS_S_COMPLETE; |
|
1534 } |
|
1535 return GSS_S_FAILURE; |
|
1536 } |
|
1537 |
|
1538 __declspec(dllexport) OM_uint32 |
|
1539 gss_release_oid_set(OM_uint32 *minor_status, |
|
1540 gss_OID_set *set) |
|
1541 { |
|
1542 PP(">>>> Calling gss_release_oid_set..."); |
|
1543 if (set == NULL || *set == GSS_C_NO_OID_SET) { |
|
1544 return GSS_S_COMPLETE; |
|
1545 } |
|
1546 for (int i = 0; i < (*set)->count; i++) { |
|
1547 delete[] (*set)->elements[i].elements; |
|
1548 } |
|
1549 delete[] (*set)->elements; |
|
1550 delete *set; |
|
1551 *set = GSS_C_NO_OID_SET; |
|
1552 return GSS_S_COMPLETE; |
|
1553 } |
|
1554 |
|
1555 __declspec(dllexport) OM_uint32 |
|
1556 gss_release_buffer(OM_uint32 *minor_status, |
|
1557 gss_buffer_t buffer) |
|
1558 { |
|
1559 PP(">>>> Calling gss_release_buffer..."); |
|
1560 if (buffer == NULL || buffer == GSS_C_NO_BUFFER) { |
|
1561 return GSS_S_COMPLETE; |
|
1562 } |
|
1563 if (buffer->value) { |
|
1564 delete[] buffer->value; |
|
1565 buffer->value = NULL; |
|
1566 } |
|
1567 buffer->length = 0; |
|
1568 return GSS_S_COMPLETE; |
|
1569 } |
|
1570 |
|
1571 /* End implemented section */ |
|
1572 |
|
1573 #ifdef __cplusplus |
|
1574 } |
|
1575 #endif |