|
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_const_OID o2, gss_const_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_const_OID_set set, gss_const_OID oid) |
|
220 { |
|
221 for (size_t 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 show_oid(gss_const_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 show_oid_set(gss_const_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 (size_t i = 0; i < mechs->count; i++) { |
|
261 show_oid(&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 gss_const_buffer_t input_name_buffer, |
|
336 gss_const_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 if (len < 4 || input[0] != 4 || input[1] != 1 || input[2] != 0) { |
|
348 return GSS_S_FAILURE; |
|
349 } |
|
350 int mechLen = (int)input[3]; /* including 06 len */ |
|
351 len -= mechLen + 8; /* 4 header bytes, and an int32 length after OID */ |
|
352 if (len <= 0) { |
|
353 return GSS_S_FAILURE; |
|
354 } |
|
355 // Reject if mech is not krb5 |
|
356 if (mechLen - 2!= KRB5_OID.length || |
|
357 memcmp(input + 6, KRB5_OID.elements, mechLen - 2)) { |
|
358 return GSS_S_FAILURE;; |
|
359 } |
|
360 input = input + mechLen + 8; |
|
361 } |
|
362 |
|
363 SEC_WCHAR* value = new SEC_WCHAR[len + 1]; |
|
364 if (value == NULL) { |
|
365 goto err; |
|
366 } |
|
367 |
|
368 len = MultiByteToWideChar(CP_UTF8, 0, input, len, value, len+1); |
|
369 if (len == 0) { |
|
370 goto err; |
|
371 } |
|
372 value[len] = 0; |
|
373 |
|
374 PP("import_name from %ls", value); |
|
375 |
|
376 if (len > 33 && !wcscmp(value+len-33, L"@WELLKNOWN:ORG.H5L.REFERALS-REALM")) { |
|
377 // Remove the wellknown referrals realms |
|
378 value[len-33] = 0; |
|
379 len -= 33; |
|
380 } else if (value[len-1] == L'@') { |
|
381 // Remove the empty realm. It might come from an NT_EXPORT_NAME. |
|
382 value[len-1] = 0; |
|
383 len--; |
|
384 } |
|
385 if (len == 0) { |
|
386 goto err; |
|
387 } |
|
388 |
|
389 if (input_name_type != NULL |
|
390 && is_same_oid(input_name_type, &HOST_SERVICE_NAME_OID)) { |
|
391 // HOST_SERVICE_NAME_OID takes the form of service@host. |
|
392 for (int i = 0; i < len; i++) { |
|
393 if (value[i] == L'\\') { |
|
394 i++; |
|
395 continue; |
|
396 } |
|
397 if (value[i] == L'@') { |
|
398 value[i] = L'/'; |
|
399 break; |
|
400 } |
|
401 } |
|
402 PP("Host-based service now %ls", value); |
|
403 } |
|
404 PP("import_name to %ls", value); |
|
405 gss_name_struct* name = new gss_name_struct; |
|
406 if (name == NULL) { |
|
407 goto err; |
|
408 } |
|
409 name->name = value; |
|
410 *output_name = (gss_name_t) name; |
|
411 return GSS_S_COMPLETE; |
|
412 err: |
|
413 if (value != NULL) { |
|
414 delete[] value; |
|
415 } |
|
416 return GSS_S_FAILURE; |
|
417 } |
|
418 |
|
419 __declspec(dllexport) OM_uint32 |
|
420 gss_compare_name(OM_uint32 *minor_status, |
|
421 gss_const_name_t name1, |
|
422 gss_const_name_t name2, |
|
423 int *name_equal) |
|
424 { |
|
425 PP(">>>> Calling gss_compare_name..."); |
|
426 CHECK_NAME(name1) |
|
427 CHECK_NAME(name2) |
|
428 CHECK_OUTPUT(name_equal) |
|
429 |
|
430 *name_equal = 0; |
|
431 |
|
432 SEC_WCHAR* n1 = name1->name; |
|
433 SEC_WCHAR* n2 = name2->name; |
|
434 PP("Comparing %ls and %ls", n1, n2); |
|
435 |
|
436 int l1 = lstrlen(n1); |
|
437 int l2 = lstrlen(n2); |
|
438 int r1 = l1; // position of @ or the end if none |
|
439 int r2 = l2; |
|
440 int i; |
|
441 |
|
442 for (i = 0; i < l1; i++) { |
|
443 if (n1[i] == L'\\') { |
|
444 i++; |
|
445 continue; |
|
446 } |
|
447 if (n1[i] == L'@') { |
|
448 r1 = i; |
|
449 break; |
|
450 } |
|
451 } |
|
452 |
|
453 for (i = 0; i < l2; i++) { |
|
454 if (n2[i] == L'\\') { |
|
455 i++; |
|
456 continue; |
|
457 } |
|
458 if (n2[i] == L'@') { |
|
459 r2 = i; |
|
460 break; |
|
461 } |
|
462 } |
|
463 |
|
464 if (l1 < l2 && l1 != r2 |
|
465 || l2 < l1 && l2 != l1) { |
|
466 return GSS_S_COMPLETE; // different |
|
467 } |
|
468 |
|
469 if (l1 > l2) { |
|
470 l1 = l2; // choose the smaller one. longer=smaller @ ... |
|
471 } |
|
472 |
|
473 // Two names are equal if they are the same or one has no realm and |
|
474 // one has realm but they have the same name. If both have realm but |
|
475 // different, they are treated different even if the names are the same. |
|
476 // Note: the default name concept is not used here. |
|
477 // Principal names on Windows are case-insensitive, both user name |
|
478 // and service principal name. |
|
479 if (CompareStringEx(LOCALE_NAME_SYSTEM_DEFAULT, NORM_IGNORECASE, |
|
480 n1, l1, n2, l1, NULL, NULL, 0) == CSTR_EQUAL) { |
|
481 *name_equal = 1; |
|
482 } |
|
483 return GSS_S_COMPLETE; |
|
484 } |
|
485 |
|
486 __declspec(dllexport) OM_uint32 |
|
487 gss_canonicalize_name(OM_uint32 *minor_status, |
|
488 gss_const_name_t input_name, |
|
489 gss_const_OID mech_type, |
|
490 gss_name_t *output_name) |
|
491 { |
|
492 PP(">>>> Calling gss_canonicalize_name..."); |
|
493 CHECK_NAME(input_name) |
|
494 CHECK_OID(mech_type) |
|
495 CHECK_OUTPUT(output_name) |
|
496 |
|
497 if (!is_same_oid(mech_type, &KRB5_OID)) { |
|
498 PP("Cannot canonicalize to non-krb5 OID"); |
|
499 return GSS_S_BAD_MECH; |
|
500 } |
|
501 gss_name_t names2 = new gss_name_struct; |
|
502 if (names2 == NULL) { |
|
503 return GSS_S_FAILURE; |
|
504 } |
|
505 names2->name = get_full_name(input_name->name); |
|
506 if (names2->name == NULL) { |
|
507 delete names2; |
|
508 return GSS_S_FAILURE; |
|
509 } |
|
510 *output_name = names2; |
|
511 return GSS_S_COMPLETE; |
|
512 } |
|
513 |
|
514 __declspec(dllexport) OM_uint32 |
|
515 gss_export_name(OM_uint32 *minor_status, |
|
516 gss_const_name_t input_name, |
|
517 gss_buffer_t exported_name) |
|
518 { |
|
519 PP(">>>> Calling gss_export_name..."); |
|
520 CHECK_NAME(input_name) |
|
521 CHECK_OUTPUT(exported_name) |
|
522 |
|
523 OM_uint32 result = GSS_S_FAILURE; |
|
524 SEC_WCHAR* name = input_name->name; |
|
525 SEC_WCHAR* fullname = get_full_name(name); |
|
526 if (!fullname) { |
|
527 goto err; |
|
528 } |
|
529 PP("Make fullname: %ls -> %ls", name, fullname); |
|
530 int len; |
|
531 size_t namelen = wcslen(fullname); |
|
532 if (namelen > 255) { |
|
533 goto err; |
|
534 } |
|
535 len = (int)namelen; |
|
536 // We only deal with not-so-long names. |
|
537 // 04 01 00 ** 06 ** OID len:int32 name |
|
538 int mechLen = KRB5_OID.length; |
|
539 char* buffer = new char[10 + mechLen + len]; |
|
540 if (buffer == NULL) { |
|
541 goto err; |
|
542 } |
|
543 buffer[0] = 4; |
|
544 buffer[1] = 1; |
|
545 buffer[2] = 0; |
|
546 buffer[3] = 2 + mechLen; |
|
547 buffer[4] = 6; |
|
548 buffer[5] = mechLen; |
|
549 memcpy_s(buffer + 6, mechLen, KRB5_OID.elements, mechLen); |
|
550 buffer[6 + mechLen] = buffer[7 + mechLen] = buffer[8 + mechLen] = 0; |
|
551 buffer[9 + mechLen] = (char)len; |
|
552 len = WideCharToMultiByte(CP_UTF8, 0, fullname, len, |
|
553 buffer+10+mechLen, len, NULL, NULL); |
|
554 if (len == 0) { |
|
555 delete[] buffer; |
|
556 goto err; |
|
557 } |
|
558 exported_name->length = 10 + mechLen + len; |
|
559 exported_name->value = buffer; |
|
560 result = GSS_S_COMPLETE; |
|
561 err: |
|
562 if (fullname != name) { |
|
563 delete[] fullname; |
|
564 } |
|
565 return result; |
|
566 } |
|
567 |
|
568 __declspec(dllexport) OM_uint32 |
|
569 gss_display_name(OM_uint32 *minor_status, |
|
570 gss_const_name_t input_name, |
|
571 gss_buffer_t output_name_buffer, |
|
572 gss_OID *output_name_type) |
|
573 { |
|
574 PP(">>>> Calling gss_display_name..."); |
|
575 CHECK_NAME(input_name) |
|
576 CHECK_OUTPUT(output_name_buffer) |
|
577 |
|
578 SEC_WCHAR* names = input_name->name; |
|
579 int len = (int)wcslen(names); |
|
580 char* buffer = new char[4*len+1]; |
|
581 if (buffer == NULL) { |
|
582 return GSS_S_FAILURE; |
|
583 } |
|
584 len = WideCharToMultiByte(CP_UTF8, 0, names, len, buffer, 4*len, NULL, NULL); |
|
585 if (len == 0) { |
|
586 delete[] buffer; |
|
587 return GSS_S_FAILURE; |
|
588 } |
|
589 buffer[len] = 0; |
|
590 output_name_buffer->length = len; |
|
591 output_name_buffer->value = buffer; |
|
592 PP("Name found: %ls -> %d [%s]", names, len, buffer); |
|
593 if (output_name_type != NULL) { |
|
594 *output_name_type = &KRB5_NAME_OID; |
|
595 } |
|
596 return GSS_S_COMPLETE; |
|
597 } |
|
598 |
|
599 __declspec(dllexport) OM_uint32 |
|
600 gss_acquire_cred(OM_uint32 *minor_status, |
|
601 gss_const_name_t desired_name, |
|
602 OM_uint32 time_req, |
|
603 gss_const_OID_set desired_mechs, |
|
604 gss_cred_usage_t cred_usage, |
|
605 gss_cred_id_t *output_cred_handle, |
|
606 gss_OID_set *actual_mechs, |
|
607 OM_uint32 *time_rec) |
|
608 { |
|
609 PP(">>>> Calling gss_acquire_cred..."); |
|
610 CHECK_OUTPUT(output_cred_handle) |
|
611 |
|
612 SECURITY_STATUS ss; |
|
613 TimeStamp ts; |
|
614 ts.QuadPart = 0; |
|
615 cred_usage = 0; |
|
616 PP("AcquireCredentialsHandle with %d %p", cred_usage, desired_mechs); |
|
617 show_oid_set(desired_mechs); |
|
618 |
|
619 BOOLEAN reqKerberos, reqSPNEGO; |
|
620 |
|
621 if (!desired_mechs) { |
|
622 reqKerberos = reqSPNEGO = TRUE; |
|
623 } else { |
|
624 if (has_oid(desired_mechs, &KRB5_OID)) { |
|
625 PP("reqKerberos"); |
|
626 reqKerberos = TRUE; |
|
627 } |
|
628 if (has_oid(desired_mechs, &SPNEGO_OID)) { |
|
629 PP("reqSPNEGO"); |
|
630 reqSPNEGO = TRUE; |
|
631 } |
|
632 if (!reqSPNEGO && !reqKerberos) { |
|
633 return GSS_S_BAD_MECH; |
|
634 } |
|
635 } |
|
636 |
|
637 if (actual_mechs) { |
|
638 *actual_mechs = GSS_C_NO_OID_SET; |
|
639 } |
|
640 |
|
641 gss_cred_id_t cred = new_cred(); |
|
642 if (cred == NULL) { |
|
643 goto err; |
|
644 } |
|
645 |
|
646 if (reqKerberos) { |
|
647 cred->phCredK = new CredHandle; |
|
648 if (cred->phCredK == NULL) { |
|
649 goto err; |
|
650 } |
|
651 ss = AcquireCredentialsHandle( |
|
652 NULL, |
|
653 L"Kerberos", |
|
654 cred_usage == 0 ? SECPKG_CRED_BOTH : |
|
655 (cred_usage == 1 ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND), |
|
656 NULL, |
|
657 NULL, |
|
658 NULL, |
|
659 NULL, |
|
660 cred->phCredK, |
|
661 &ts); |
|
662 if (!(SEC_SUCCESS(ss))) { |
|
663 delete cred->phCredK; |
|
664 cred->phCredK = NULL; |
|
665 goto err; |
|
666 } |
|
667 } |
|
668 |
|
669 if (reqSPNEGO) { |
|
670 cred->phCredS = new CredHandle; |
|
671 if (cred->phCredS == NULL) { |
|
672 goto err; |
|
673 } |
|
674 SEC_WINNT_AUTH_IDENTITY_EX auth; |
|
675 ZeroMemory(&auth, sizeof(auth)); |
|
676 auth.Version = SEC_WINNT_AUTH_IDENTITY_VERSION; |
|
677 auth.Length = sizeof(auth); |
|
678 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; |
|
679 auth.PackageList = (unsigned short*)L"Kerberos"; |
|
680 auth.PackageListLength = 8; |
|
681 ss = AcquireCredentialsHandle( |
|
682 NULL, |
|
683 L"Negotiate", |
|
684 cred_usage == 0 ? SECPKG_CRED_BOTH : |
|
685 (cred_usage == 1 ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND), |
|
686 NULL, |
|
687 &auth, |
|
688 NULL, |
|
689 NULL, |
|
690 cred->phCredS, |
|
691 &ts); |
|
692 if (!(SEC_SUCCESS(ss))) { |
|
693 delete cred->phCredS; |
|
694 cred->phCredS = NULL; |
|
695 goto err; |
|
696 } |
|
697 } |
|
698 |
|
699 if (actual_mechs) { |
|
700 if (gss_create_empty_oid_set(minor_status, actual_mechs)) { |
|
701 goto err; |
|
702 } |
|
703 if (reqKerberos) { |
|
704 if (gss_add_oid_set_member(minor_status, &KRB5_OID, actual_mechs)) { |
|
705 goto err; |
|
706 } |
|
707 } |
|
708 if (reqSPNEGO) { |
|
709 if (gss_add_oid_set_member(minor_status, &SPNEGO_OID, actual_mechs)) { |
|
710 goto err; |
|
711 } |
|
712 } |
|
713 } |
|
714 |
|
715 *output_cred_handle = (gss_cred_id_t)cred; |
|
716 |
|
717 // Note: ts here is weirdly huge, maybe because LSA retains the |
|
718 // password and can re-acquire a TGT at anytime. It will be |
|
719 // GSSCredential.INDEFINITE_LIFETIME. |
|
720 show_time("cred expiration", &ts); |
|
721 cred->time = seconds_until(1, &ts); |
|
722 if (time_rec != NULL) { |
|
723 *time_rec = cred->time; |
|
724 } |
|
725 |
|
726 // Since only default cred is supported, if there is a desired_name, |
|
727 // we must make sure it is the same as the realname of the default cred. |
|
728 if (desired_name != NULL) { |
|
729 PP("Acquiring cred with a name. Check if it's me."); |
|
730 gss_name_t realname; |
|
731 if (gss_inquire_cred(minor_status, *output_cred_handle, &realname, |
|
732 NULL, NULL, NULL) != GSS_S_COMPLETE) { |
|
733 PP("Cannot get owner name of default creds"); |
|
734 goto err; |
|
735 } |
|
736 SEC_WCHAR* rnames = realname->name; |
|
737 SEC_WCHAR* dnames = desired_name->name; |
|
738 int equals = 0; |
|
739 gss_compare_name(minor_status, realname, desired_name, &equals); |
|
740 gss_release_name(minor_status, &realname); |
|
741 PP("Comparing result: %d", equals); |
|
742 if (!equals) { |
|
743 goto err; |
|
744 } |
|
745 } |
|
746 |
|
747 return GSS_S_COMPLETE; |
|
748 err: |
|
749 if (cred) { |
|
750 OM_uint32 dummy; |
|
751 gss_release_cred(&dummy, &cred); |
|
752 } |
|
753 if (actual_mechs) { |
|
754 OM_uint32 dummy; |
|
755 gss_release_oid_set(&dummy, actual_mechs); |
|
756 } |
|
757 return GSS_S_FAILURE; |
|
758 } |
|
759 |
|
760 __declspec(dllexport) OM_uint32 |
|
761 gss_release_cred(OM_uint32 *minor_status, |
|
762 gss_cred_id_t *cred_handle) |
|
763 { |
|
764 PP(">>>> Calling gss_release_cred..."); |
|
765 if (cred_handle && *cred_handle) { |
|
766 if ((*cred_handle)->phCredK) { |
|
767 FreeCredentialsHandle((*cred_handle)->phCredK); |
|
768 delete (*cred_handle)->phCredK; |
|
769 } |
|
770 if ((*cred_handle)->phCredS) { |
|
771 FreeCredentialsHandle((*cred_handle)->phCredS); |
|
772 delete (*cred_handle)->phCredS; |
|
773 } |
|
774 delete *cred_handle; |
|
775 *cred_handle = GSS_C_NO_CREDENTIAL; |
|
776 } |
|
777 return GSS_S_COMPLETE; |
|
778 } |
|
779 |
|
780 __declspec(dllexport) OM_uint32 |
|
781 gss_inquire_cred(OM_uint32 *minor_status, |
|
782 gss_const_cred_id_t cred_handle, |
|
783 gss_name_t *name, |
|
784 OM_uint32 *lifetime, |
|
785 gss_cred_usage_t *cred_usage, |
|
786 gss_OID_set *mechanisms) |
|
787 { |
|
788 PP(">>>> Calling gss_inquire_cred..."); |
|
789 CHECK_CRED(cred_handle) |
|
790 |
|
791 CredHandle* cred = cred_handle->phCredK |
|
792 ? cred_handle->phCredK |
|
793 : cred_handle->phCredS; |
|
794 SECURITY_STATUS ss; |
|
795 if (name) { |
|
796 *name = GSS_C_NO_NAME; |
|
797 SecPkgCredentials_Names snames; |
|
798 ss = QueryCredentialsAttributes(cred, SECPKG_CRED_ATTR_NAMES, &snames); |
|
799 if (!SEC_SUCCESS(ss)) { |
|
800 return GSS_S_FAILURE; |
|
801 } |
|
802 SEC_WCHAR* names = new SEC_WCHAR[lstrlen(snames.sUserName) + 1]; |
|
803 if (names == NULL) { |
|
804 return GSS_S_FAILURE; |
|
805 } |
|
806 StringCchCopy(names, lstrlen(snames.sUserName) + 1, snames.sUserName); |
|
807 FreeContextBuffer(snames.sUserName); |
|
808 PP("Allocate new name at %p", names); |
|
809 gss_name_t name1 = new gss_name_struct; |
|
810 if (name1 == NULL) { |
|
811 delete[] names; |
|
812 return GSS_S_FAILURE; |
|
813 } |
|
814 name1->name = names; |
|
815 *name = (gss_name_t) name1; |
|
816 } |
|
817 if (lifetime) { |
|
818 *lifetime = cred_handle->time; |
|
819 } |
|
820 if (cred_usage) { |
|
821 *cred_usage = 1; // We only support INITIATE_ONLY now |
|
822 } |
|
823 if (mechanisms) { |
|
824 // Useless for Java |
|
825 } |
|
826 // Others inquiries not supported yet |
|
827 return GSS_S_COMPLETE; |
|
828 } |
|
829 |
|
830 __declspec(dllexport) OM_uint32 |
|
831 gss_import_sec_context(OM_uint32 *minor_status, |
|
832 gss_const_buffer_t interprocess_token, |
|
833 gss_ctx_id_t *context_handle) |
|
834 { |
|
835 // Not transferable, return FAILURE |
|
836 PP(">>>> Calling UNIMPLEMENTED gss_import_sec_context..."); |
|
837 *minor_status = 0; |
|
838 return GSS_S_FAILURE; |
|
839 } |
|
840 |
|
841 __declspec(dllexport) OM_uint32 |
|
842 gss_init_sec_context(OM_uint32 *minor_status, |
|
843 gss_const_cred_id_t initiator_cred_handle, |
|
844 gss_ctx_id_t *context_handle, |
|
845 gss_const_name_t target_name, |
|
846 gss_const_OID mech_type, |
|
847 OM_uint32 req_flags, |
|
848 OM_uint32 time_req, |
|
849 gss_const_channel_bindings_t input_chan_bindings, |
|
850 gss_const_buffer_t input_token, |
|
851 gss_OID *actual_mech_type, |
|
852 gss_buffer_t output_token, |
|
853 OM_uint32 *ret_flags, |
|
854 OM_uint32 *time_rec) |
|
855 { |
|
856 PP(">>>> Calling gss_init_sec_context..."); |
|
857 CHECK_NAME(target_name) |
|
858 CHECK_OUTPUT(output_token) |
|
859 |
|
860 SECURITY_STATUS ss; |
|
861 TimeStamp lifeTime; |
|
862 SecBufferDesc inBuffDesc; |
|
863 SecBuffer inSecBuff; |
|
864 SecBufferDesc outBuffDesc; |
|
865 SecBuffer outSecBuff; |
|
866 BOOLEAN isSPNEGO = is_same_oid(mech_type, &SPNEGO_OID); |
|
867 |
|
868 gss_ctx_id_t pc; |
|
869 |
|
870 output_token->length = 0; |
|
871 output_token->value = NULL; |
|
872 |
|
873 BOOLEAN firstTime = (*context_handle == GSS_C_NO_CONTEXT); |
|
874 PP("First time? %d", firstTime); |
|
875 if (firstTime) { |
|
876 pc = new_context(isSPNEGO); |
|
877 if (pc == NULL) { |
|
878 return GSS_S_FAILURE; |
|
879 } |
|
880 *context_handle = (gss_ctx_id_t) pc; |
|
881 } else { |
|
882 pc = *context_handle; |
|
883 } |
|
884 |
|
885 if (pc == NULL) { |
|
886 return GSS_S_NO_CONTEXT; |
|
887 } |
|
888 |
|
889 DWORD outFlag; |
|
890 TCHAR outName[100]; |
|
891 |
|
892 OM_uint32 minor; |
|
893 gss_buffer_desc tn; |
|
894 gss_display_name(&minor, target_name, &tn, NULL); |
|
895 int len = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)tn.value, (int)tn.length, |
|
896 outName, sizeof(outName) - 1); |
|
897 if (len == 0) { |
|
898 goto err; |
|
899 } |
|
900 outName[len] = 0; |
|
901 |
|
902 int flag = flag_gss_to_sspi(req_flags) | ISC_REQ_ALLOCATE_MEMORY; |
|
903 |
|
904 outBuffDesc.ulVersion = SECBUFFER_VERSION; |
|
905 outBuffDesc.cBuffers = 1; |
|
906 outBuffDesc.pBuffers = &outSecBuff; |
|
907 |
|
908 outSecBuff.BufferType = SECBUFFER_TOKEN; |
|
909 |
|
910 if (!firstTime) { |
|
911 inBuffDesc.ulVersion = SECBUFFER_VERSION; |
|
912 inBuffDesc.cBuffers = 1; |
|
913 inBuffDesc.pBuffers = &inSecBuff; |
|
914 |
|
915 inSecBuff.BufferType = SECBUFFER_TOKEN; |
|
916 inSecBuff.cbBuffer = (ULONG)input_token->length; |
|
917 inSecBuff.pvBuffer = input_token->value; |
|
918 } else if (!pc->phCred) { |
|
919 if (isSPNEGO && initiator_cred_handle |
|
920 && initiator_cred_handle->phCredS) { |
|
921 PP("Find SPNEGO credentials"); |
|
922 pc->phCred = initiator_cred_handle->phCredS; |
|
923 pc->isLocalCred = FALSE; |
|
924 } else if (!isSPNEGO && initiator_cred_handle |
|
925 && initiator_cred_handle->phCredK) { |
|
926 PP("Find Kerberos credentials"); |
|
927 pc->phCred = initiator_cred_handle->phCredK; |
|
928 pc->isLocalCred = FALSE; |
|
929 } else { |
|
930 PP("No credentials provided, acquire myself"); |
|
931 CredHandle* newCred = new CredHandle; |
|
932 SEC_WINNT_AUTH_IDENTITY_EX auth; |
|
933 ZeroMemory(&auth, sizeof(auth)); |
|
934 auth.Version = SEC_WINNT_AUTH_IDENTITY_VERSION; |
|
935 auth.Length = sizeof(auth); |
|
936 auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; |
|
937 auth.PackageList = (unsigned short*)L"Kerberos"; |
|
938 auth.PackageListLength = 8; |
|
939 ss = AcquireCredentialsHandle( |
|
940 NULL, |
|
941 isSPNEGO ? L"Negotiate" : L"Kerberos", |
|
942 SECPKG_CRED_OUTBOUND, |
|
943 NULL, |
|
944 isSPNEGO ? &auth : NULL, |
|
945 NULL, |
|
946 NULL, |
|
947 newCred, |
|
948 &lifeTime); |
|
949 if (!(SEC_SUCCESS(ss))) { |
|
950 delete newCred; |
|
951 goto err; |
|
952 } |
|
953 pc->phCred = newCred; |
|
954 pc->isLocalCred = TRUE; |
|
955 } |
|
956 } |
|
957 ss = InitializeSecurityContext( |
|
958 pc->phCred, |
|
959 firstTime ? NULL : &pc->hCtxt, |
|
960 outName, |
|
961 flag, |
|
962 0, |
|
963 SECURITY_NATIVE_DREP, |
|
964 firstTime ? NULL : &inBuffDesc, |
|
965 0, |
|
966 &pc->hCtxt, |
|
967 &outBuffDesc, |
|
968 &outFlag, |
|
969 &lifeTime); |
|
970 |
|
971 if (!SEC_SUCCESS(ss)) { |
|
972 PP("InitializeSecurityContext failed"); |
|
973 goto err; |
|
974 } |
|
975 |
|
976 pc->flags = *ret_flags = flag_sspi_to_gss(outFlag); |
|
977 |
|
978 // Ignore the result of the next call. Might fail before context established. |
|
979 QueryContextAttributes( |
|
980 &pc->hCtxt, SECPKG_ATTR_SIZES, &pc->SecPkgContextSizes); |
|
981 PP("cbMaxSignature: %ld. cbBlockSize: %ld. cbSecurityTrailer: %ld", |
|
982 pc->SecPkgContextSizes.cbMaxSignature, |
|
983 pc->SecPkgContextSizes.cbBlockSize, |
|
984 pc->SecPkgContextSizes.cbSecurityTrailer); |
|
985 |
|
986 output_token->length = outSecBuff.cbBuffer; |
|
987 if (outSecBuff.cbBuffer) { |
|
988 // No idea how user would free the data. Let's duplicate one. |
|
989 output_token->value = new char[outSecBuff.cbBuffer]; |
|
990 if (!output_token->value) { |
|
991 FreeContextBuffer(outSecBuff.pvBuffer); |
|
992 output_token->length = 0; |
|
993 goto err; |
|
994 } |
|
995 memcpy(output_token->value, outSecBuff.pvBuffer, outSecBuff.cbBuffer); |
|
996 FreeContextBuffer(outSecBuff.pvBuffer); |
|
997 } |
|
998 |
|
999 if (ss == SEC_I_CONTINUE_NEEDED) { |
|
1000 return GSS_S_CONTINUE_NEEDED; |
|
1001 } else { |
|
1002 pc->established = true; |
|
1003 ss = QueryContextAttributes(&pc->hCtxt, SECPKG_ATTR_NATIVE_NAMES, &pc->nnames); |
|
1004 if (!SEC_SUCCESS(ss)) { |
|
1005 goto err; |
|
1006 } |
|
1007 PP("Names. %ls %ls", pc->nnames.sClientName, pc->nnames.sServerName); |
|
1008 *ret_flags |= GSS_C_PROT_READY_FLAG; |
|
1009 return GSS_S_COMPLETE; |
|
1010 } |
|
1011 err: |
|
1012 if (firstTime) { |
|
1013 OM_uint32 dummy; |
|
1014 gss_delete_sec_context(&dummy, context_handle, GSS_C_NO_BUFFER); |
|
1015 } |
|
1016 if (output_token->value) { |
|
1017 gss_release_buffer(NULL, output_token); |
|
1018 output_token = GSS_C_NO_BUFFER; |
|
1019 } |
|
1020 return GSS_S_FAILURE; |
|
1021 } |
|
1022 |
|
1023 __declspec(dllexport) OM_uint32 |
|
1024 gss_accept_sec_context(OM_uint32 *minor_status, |
|
1025 gss_ctx_id_t *context_handle, |
|
1026 gss_const_cred_id_t acceptor_cred_handle, |
|
1027 gss_const_buffer_t input_token, |
|
1028 gss_const_channel_bindings_t input_chan_bindings, |
|
1029 gss_name_t *src_name, |
|
1030 gss_OID *mech_type, |
|
1031 gss_buffer_t output_token, |
|
1032 OM_uint32 *ret_flags, |
|
1033 OM_uint32 *time_rec, |
|
1034 gss_cred_id_t *delegated_cred_handle) |
|
1035 { |
|
1036 PP(">>>> Calling UNIMPLEMENTED gss_accept_sec_context..."); |
|
1037 PP("gss_accept_sec_context is not supported in this initiator-only library"); |
|
1038 return GSS_S_FAILURE; |
|
1039 } |
|
1040 |
|
1041 __declspec(dllexport) OM_uint32 |
|
1042 gss_inquire_context(OM_uint32 *minor_status, |
|
1043 gss_const_ctx_id_t context_handle, |
|
1044 gss_name_t *src_name, |
|
1045 gss_name_t *targ_name, |
|
1046 OM_uint32 *lifetime_rec, |
|
1047 gss_OID *mech_type, |
|
1048 OM_uint32 *ctx_flags, |
|
1049 int *locally_initiated, |
|
1050 int *open) |
|
1051 { |
|
1052 PP(">>>> Calling gss_inquire_context..."); |
|
1053 CHECK_CONTEXT(context_handle) |
|
1054 |
|
1055 gss_name_t n1 = NULL; |
|
1056 gss_name_t n2 = NULL; |
|
1057 if (!context_handle->established) { |
|
1058 return GSS_S_NO_CONTEXT; |
|
1059 } |
|
1060 if (src_name != NULL) { |
|
1061 n1 = new gss_name_struct; |
|
1062 if (n1 == NULL) { |
|
1063 goto err; |
|
1064 } |
|
1065 n1->name = new SEC_WCHAR[lstrlen(context_handle->nnames.sClientName) + 1]; |
|
1066 if (n1->name == NULL) { |
|
1067 goto err; |
|
1068 } |
|
1069 PP("Allocate new name at %p", n1->name); |
|
1070 StringCchCopy(n1->name, lstrlen(context_handle->nnames.sClientName) + 1, |
|
1071 context_handle->nnames.sClientName); |
|
1072 *src_name = (gss_name_t) n1; |
|
1073 } |
|
1074 if (targ_name != NULL) { |
|
1075 n2 = new gss_name_struct; |
|
1076 if (n2 == NULL) { |
|
1077 goto err; |
|
1078 } |
|
1079 n2->name = new SEC_WCHAR[lstrlen(context_handle->nnames.sServerName) + 1]; |
|
1080 if (n2->name == NULL) { |
|
1081 goto err; |
|
1082 } |
|
1083 PP("Allocate new name at %p", n2->name); |
|
1084 StringCchCopy(n2->name, lstrlen(context_handle->nnames.sServerName) + 1, |
|
1085 context_handle->nnames.sServerName); |
|
1086 *targ_name = (gss_name_t) n2; |
|
1087 } |
|
1088 if (lifetime_rec != NULL) { |
|
1089 SecPkgContext_Lifespan ls; |
|
1090 SECURITY_STATUS ss; |
|
1091 ss = QueryContextAttributes( |
|
1092 (PCtxtHandle)&context_handle->hCtxt, |
|
1093 SECPKG_ATTR_LIFESPAN, |
|
1094 &ls); |
|
1095 if (!SEC_SUCCESS(ss)) { |
|
1096 goto err; |
|
1097 } |
|
1098 *lifetime_rec = seconds_until(0, &ls.tsExpiry); |
|
1099 } |
|
1100 if (mech_type != NULL) { |
|
1101 *mech_type = context_handle->isSPNEGO |
|
1102 ? &SPNEGO_OID : &KRB5_OID; |
|
1103 } |
|
1104 if (ctx_flags != NULL) { |
|
1105 *ctx_flags = context_handle->flags; |
|
1106 } |
|
1107 if (locally_initiated != NULL) { |
|
1108 // We are always initiator |
|
1109 *locally_initiated = 1; |
|
1110 } |
|
1111 return GSS_S_COMPLETE; |
|
1112 err: |
|
1113 if (n1 != NULL) { |
|
1114 if (n1->name != NULL) { |
|
1115 delete[] n1->name; |
|
1116 } |
|
1117 delete n1; |
|
1118 n1 = NULL; |
|
1119 } |
|
1120 if (n2 != NULL) { |
|
1121 if (n2->name != NULL) { |
|
1122 delete[] n2->name; |
|
1123 } |
|
1124 delete n2; |
|
1125 n2 = NULL; |
|
1126 } |
|
1127 return GSS_S_FAILURE; |
|
1128 } |
|
1129 |
|
1130 __declspec(dllexport) OM_uint32 |
|
1131 gss_delete_sec_context(OM_uint32 *minor_status, |
|
1132 gss_ctx_id_t *context_handle, |
|
1133 gss_buffer_t output_token) |
|
1134 { |
|
1135 PP(">>>> Calling gss_delete_sec_context..."); |
|
1136 CHECK_CONTEXT(context_handle) |
|
1137 |
|
1138 DeleteSecurityContext(&(*context_handle)->hCtxt); |
|
1139 if ((*context_handle)->isLocalCred && (*context_handle)->phCred != NULL) { |
|
1140 FreeCredentialsHandle((*context_handle)->phCred); |
|
1141 (*context_handle)->phCred = NULL; |
|
1142 } |
|
1143 if ((*context_handle)->nnames.sClientName != NULL) { |
|
1144 FreeContextBuffer((*context_handle)->nnames.sClientName); |
|
1145 (*context_handle)->nnames.sClientName = NULL; |
|
1146 } |
|
1147 if ((*context_handle)->nnames.sServerName != NULL) { |
|
1148 FreeContextBuffer((*context_handle)->nnames.sServerName); |
|
1149 (*context_handle)->nnames.sServerName = NULL; |
|
1150 } |
|
1151 delete (*context_handle); |
|
1152 *context_handle = GSS_C_NO_CONTEXT; |
|
1153 return GSS_S_COMPLETE; |
|
1154 } |
|
1155 |
|
1156 __declspec(dllexport) OM_uint32 |
|
1157 gss_context_time(OM_uint32 *minor_status, |
|
1158 gss_const_ctx_id_t context_handle, |
|
1159 OM_uint32 *time_rec) |
|
1160 { |
|
1161 PP(">>>> Calling IMPLEMENTED gss_context_time..."); |
|
1162 CHECK_CONTEXT(context_handle) |
|
1163 CHECK_OUTPUT(time_rec) |
|
1164 |
|
1165 SECURITY_STATUS ss; |
|
1166 SecPkgContext_Lifespan ls; |
|
1167 ss = QueryContextAttributes( |
|
1168 (PCtxtHandle)&context_handle->hCtxt, |
|
1169 SECPKG_ATTR_LIFESPAN, |
|
1170 &ls); |
|
1171 if (ss == SEC_E_OK) { |
|
1172 *time_rec = seconds_until(0, &ls.tsExpiry); |
|
1173 show_time("context start", &ls.tsStart); |
|
1174 show_time("context expiry", &ls.tsExpiry); |
|
1175 return *time_rec == 0 ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; |
|
1176 } else { |
|
1177 return GSS_S_FAILURE; |
|
1178 } |
|
1179 } |
|
1180 |
|
1181 __declspec(dllexport) OM_uint32 |
|
1182 gss_wrap_size_limit(OM_uint32 *minor_status, |
|
1183 gss_const_ctx_id_t context_handle, |
|
1184 int conf_req_flag, |
|
1185 gss_qop_t qop_req, |
|
1186 OM_uint32 req_output_size, |
|
1187 OM_uint32 *max_input_size) |
|
1188 { |
|
1189 PP(">>>> Calling gss_wrap_size_limit..."); |
|
1190 CHECK_CONTEXT(context_handle) |
|
1191 CHECK_OUTPUT(max_input_size) |
|
1192 |
|
1193 *max_input_size = req_output_size |
|
1194 - context_handle->SecPkgContextSizes.cbSecurityTrailer |
|
1195 - context_handle->SecPkgContextSizes.cbBlockSize; |
|
1196 return GSS_S_COMPLETE; |
|
1197 } |
|
1198 |
|
1199 __declspec(dllexport) OM_uint32 |
|
1200 gss_export_sec_context(OM_uint32 *minor_status, |
|
1201 gss_ctx_id_t *context_handle, |
|
1202 gss_buffer_t interprocess_token) |
|
1203 { |
|
1204 PP(">>>> Calling UNIMPLEMENTED gss_export_sec_context..."); |
|
1205 return GSS_S_FAILURE; |
|
1206 } |
|
1207 |
|
1208 __declspec(dllexport) OM_uint32 |
|
1209 gss_get_mic(OM_uint32 *minor_status, |
|
1210 gss_const_ctx_id_t context_handle, |
|
1211 gss_qop_t qop_req, |
|
1212 gss_const_buffer_t message_buffer, |
|
1213 gss_buffer_t msg_token) |
|
1214 { |
|
1215 PP(">>>> Calling gss_get_mic..."); |
|
1216 CHECK_CONTEXT(context_handle) |
|
1217 CHECK_BUFFER(message_buffer) |
|
1218 CHECK_OUTPUT(msg_token) |
|
1219 |
|
1220 SECURITY_STATUS ss; |
|
1221 SecBufferDesc buffDesc; |
|
1222 SecBuffer secBuff[2]; |
|
1223 |
|
1224 buffDesc.cBuffers = 2; |
|
1225 buffDesc.pBuffers = secBuff; |
|
1226 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1227 |
|
1228 secBuff[0].BufferType = SECBUFFER_DATA; |
|
1229 secBuff[0].cbBuffer = (ULONG)message_buffer->length; |
|
1230 secBuff[0].pvBuffer = message_buffer->value; |
|
1231 |
|
1232 secBuff[1].BufferType = SECBUFFER_TOKEN; |
|
1233 secBuff[1].cbBuffer = context_handle->SecPkgContextSizes.cbMaxSignature; |
|
1234 secBuff[1].pvBuffer = msg_token->value = new char[secBuff[1].cbBuffer]; |
|
1235 |
|
1236 ss = MakeSignature((PCtxtHandle)&context_handle->hCtxt, 0, &buffDesc, 0); |
|
1237 |
|
1238 if (!SEC_SUCCESS(ss)) { |
|
1239 msg_token->length = 0; |
|
1240 msg_token->value = NULL; |
|
1241 delete[] secBuff[1].pvBuffer; |
|
1242 return GSS_S_FAILURE; |
|
1243 } |
|
1244 |
|
1245 msg_token->length = secBuff[1].cbBuffer; |
|
1246 return GSS_S_COMPLETE; |
|
1247 } |
|
1248 |
|
1249 __declspec(dllexport) OM_uint32 |
|
1250 gss_verify_mic(OM_uint32 *minor_status, |
|
1251 gss_const_ctx_id_t context_handle, |
|
1252 gss_const_buffer_t message_buffer, |
|
1253 gss_const_buffer_t token_buffer, |
|
1254 gss_qop_t *qop_state) |
|
1255 { |
|
1256 PP(">>>> Calling gss_verify_mic..."); |
|
1257 CHECK_CONTEXT(context_handle) |
|
1258 CHECK_BUFFER(message_buffer) |
|
1259 CHECK_BUFFER(token_buffer) |
|
1260 |
|
1261 SECURITY_STATUS ss; |
|
1262 SecBufferDesc buffDesc; |
|
1263 SecBuffer secBuff[2]; |
|
1264 ULONG qop; |
|
1265 |
|
1266 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1267 buffDesc.cBuffers = 2; |
|
1268 buffDesc.pBuffers = secBuff; |
|
1269 |
|
1270 secBuff[0].BufferType = SECBUFFER_TOKEN; |
|
1271 secBuff[0].cbBuffer = (ULONG)token_buffer->length; |
|
1272 secBuff[0].pvBuffer = token_buffer->value; |
|
1273 |
|
1274 secBuff[1].BufferType = SECBUFFER_DATA; |
|
1275 secBuff[1].cbBuffer = (ULONG)message_buffer->length; |
|
1276 secBuff[1].pvBuffer = message_buffer->value; |
|
1277 |
|
1278 ss = VerifySignature((PCtxtHandle)&context_handle->hCtxt, &buffDesc, 0, &qop); |
|
1279 if (qop_state) { |
|
1280 *qop_state = qop; |
|
1281 } |
|
1282 |
|
1283 if (ss == SEC_E_OK) { |
|
1284 return GSS_S_COMPLETE; |
|
1285 } else if (ss == SEC_E_OUT_OF_SEQUENCE) { |
|
1286 return GSS_S_UNSEQ_TOKEN; |
|
1287 } else { |
|
1288 return GSS_S_BAD_SIG; |
|
1289 } |
|
1290 } |
|
1291 |
|
1292 __declspec(dllexport) OM_uint32 |
|
1293 gss_wrap(OM_uint32 *minor_status, |
|
1294 gss_const_ctx_id_t context_handle, |
|
1295 int conf_req_flag, |
|
1296 gss_qop_t qop_req, |
|
1297 gss_const_buffer_t input_message_buffer, |
|
1298 int *conf_state, |
|
1299 gss_buffer_t output_message_buffer) |
|
1300 { |
|
1301 PP(">>>> Calling gss_wrap..."); |
|
1302 CHECK_CONTEXT(context_handle) |
|
1303 CHECK_BUFFER(input_message_buffer) |
|
1304 CHECK_OUTPUT(output_message_buffer) |
|
1305 |
|
1306 SECURITY_STATUS ss; |
|
1307 SecBufferDesc buffDesc; |
|
1308 SecBuffer secBuff[3]; |
|
1309 |
|
1310 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1311 buffDesc.cBuffers = 3; |
|
1312 buffDesc.pBuffers = secBuff; |
|
1313 |
|
1314 secBuff[0].BufferType = SECBUFFER_TOKEN; |
|
1315 secBuff[0].cbBuffer = context_handle->SecPkgContextSizes.cbSecurityTrailer; |
|
1316 output_message_buffer->value = secBuff[0].pvBuffer = malloc( |
|
1317 context_handle->SecPkgContextSizes.cbSecurityTrailer |
|
1318 + input_message_buffer->length |
|
1319 + context_handle->SecPkgContextSizes.cbBlockSize);; |
|
1320 |
|
1321 secBuff[1].BufferType = SECBUFFER_DATA; |
|
1322 secBuff[1].cbBuffer = (ULONG)input_message_buffer->length; |
|
1323 secBuff[1].pvBuffer = malloc(secBuff[1].cbBuffer); |
|
1324 memcpy_s(secBuff[1].pvBuffer, secBuff[1].cbBuffer, |
|
1325 input_message_buffer->value, input_message_buffer->length); |
|
1326 |
|
1327 secBuff[2].BufferType = SECBUFFER_PADDING; |
|
1328 secBuff[2].cbBuffer = context_handle->SecPkgContextSizes.cbBlockSize; |
|
1329 secBuff[2].pvBuffer = malloc(secBuff[2].cbBuffer); |
|
1330 |
|
1331 ss = EncryptMessage((PCtxtHandle)&context_handle->hCtxt, |
|
1332 conf_req_flag ? 0 : SECQOP_WRAP_NO_ENCRYPT, |
|
1333 &buffDesc, 0); |
|
1334 if (conf_state) { |
|
1335 *conf_state = conf_req_flag; |
|
1336 } |
|
1337 |
|
1338 if (!SEC_SUCCESS(ss)) { |
|
1339 free(secBuff[0].pvBuffer); |
|
1340 free(secBuff[1].pvBuffer); |
|
1341 free(secBuff[2].pvBuffer); |
|
1342 output_message_buffer->length = 0; |
|
1343 output_message_buffer->value = NULL; |
|
1344 return GSS_S_FAILURE; |
|
1345 } |
|
1346 |
|
1347 memcpy_s((PBYTE)secBuff[0].pvBuffer + secBuff[0].cbBuffer, |
|
1348 input_message_buffer->length + context_handle->SecPkgContextSizes.cbBlockSize, |
|
1349 secBuff[1].pvBuffer, |
|
1350 secBuff[1].cbBuffer); |
|
1351 memcpy_s((PBYTE)secBuff[0].pvBuffer + secBuff[0].cbBuffer + secBuff[1].cbBuffer, |
|
1352 context_handle->SecPkgContextSizes.cbBlockSize, |
|
1353 secBuff[2].pvBuffer, |
|
1354 secBuff[2].cbBuffer); |
|
1355 |
|
1356 output_message_buffer->length = secBuff[0].cbBuffer + secBuff[1].cbBuffer |
|
1357 + secBuff[2].cbBuffer; |
|
1358 free(secBuff[1].pvBuffer); |
|
1359 free(secBuff[2].pvBuffer); |
|
1360 |
|
1361 return GSS_S_COMPLETE; |
|
1362 } |
|
1363 |
|
1364 __declspec(dllexport) OM_uint32 |
|
1365 gss_unwrap(OM_uint32 *minor_status, |
|
1366 gss_const_ctx_id_t context_handle, |
|
1367 gss_const_buffer_t input_message_buffer, |
|
1368 gss_buffer_t output_message_buffer, |
|
1369 int *conf_state, |
|
1370 gss_qop_t *qop_state) |
|
1371 { |
|
1372 PP(">>>> Calling gss_unwrap..."); |
|
1373 CHECK_CONTEXT(context_handle) |
|
1374 CHECK_BUFFER(input_message_buffer) |
|
1375 CHECK_OUTPUT(output_message_buffer) |
|
1376 |
|
1377 SECURITY_STATUS ss; |
|
1378 SecBufferDesc buffDesc; |
|
1379 SecBuffer secBuff[2]; |
|
1380 ULONG ulQop = 0; |
|
1381 |
|
1382 buffDesc.cBuffers = 2; |
|
1383 buffDesc.pBuffers = secBuff; |
|
1384 buffDesc.ulVersion = SECBUFFER_VERSION; |
|
1385 |
|
1386 secBuff[0].BufferType = SECBUFFER_STREAM; |
|
1387 secBuff[0].cbBuffer = (ULONG)input_message_buffer->length; |
|
1388 secBuff[0].pvBuffer = malloc(input_message_buffer->length); |
|
1389 memcpy_s(secBuff[0].pvBuffer, input_message_buffer->length, |
|
1390 input_message_buffer->value, input_message_buffer->length); |
|
1391 |
|
1392 secBuff[1].BufferType = SECBUFFER_DATA; |
|
1393 secBuff[1].cbBuffer = 0; |
|
1394 secBuff[1].pvBuffer = NULL; |
|
1395 |
|
1396 ss = DecryptMessage((PCtxtHandle)&context_handle->hCtxt, &buffDesc, 0, &ulQop); |
|
1397 if (qop_state) { |
|
1398 *qop_state = ulQop; |
|
1399 } |
|
1400 if (!SEC_SUCCESS(ss)) { |
|
1401 free(secBuff[0].pvBuffer); |
|
1402 output_message_buffer->length = 0; |
|
1403 output_message_buffer->value = NULL; |
|
1404 return GSS_S_FAILURE; |
|
1405 } |
|
1406 |
|
1407 // Must allocate a new memory block so client can release it correctly |
|
1408 output_message_buffer->length = secBuff[1].cbBuffer; |
|
1409 output_message_buffer->value = new char[secBuff[1].cbBuffer]; |
|
1410 memcpy_s(output_message_buffer->value, secBuff[1].cbBuffer, |
|
1411 secBuff[1].pvBuffer, secBuff[1].cbBuffer); |
|
1412 *conf_state = ulQop == SECQOP_WRAP_NO_ENCRYPT ? 0 : 1; |
|
1413 |
|
1414 free(secBuff[0].pvBuffer); |
|
1415 return GSS_S_COMPLETE; |
|
1416 } |
|
1417 |
|
1418 __declspec(dllexport) OM_uint32 |
|
1419 gss_indicate_mechs(OM_uint32 *minor_status, |
|
1420 gss_OID_set *mech_set) |
|
1421 { |
|
1422 PP(">>>> Calling gss_indicate_mechs..."); |
|
1423 OM_uint32 major = GSS_S_COMPLETE; |
|
1424 |
|
1425 ULONG ccPackages; |
|
1426 PSecPkgInfo packages; |
|
1427 EnumerateSecurityPackages(&ccPackages, &packages); |
|
1428 PP("EnumerateSecurityPackages returns %ld", ccPackages); |
|
1429 for (unsigned int i = 0; i < ccPackages; i++) { |
|
1430 PP("#%d: %ls, %ls\n", i, packages[i].Name, packages[i].Comment); |
|
1431 } |
|
1432 FreeContextBuffer(packages); |
|
1433 |
|
1434 // Hardcode kerberos and SPNEGO support |
|
1435 major = gss_create_empty_oid_set(minor_status, mech_set); |
|
1436 if (major != GSS_S_COMPLETE) { |
|
1437 goto done; |
|
1438 } |
|
1439 |
|
1440 major = gss_add_oid_set_member(minor_status, &KRB5_OID, mech_set); |
|
1441 if (major != GSS_S_COMPLETE) { |
|
1442 goto done; |
|
1443 } |
|
1444 |
|
1445 major = gss_add_oid_set_member(minor_status, &SPNEGO_OID, mech_set); |
|
1446 if (major != GSS_S_COMPLETE) { |
|
1447 goto done; |
|
1448 } |
|
1449 |
|
1450 done: |
|
1451 |
|
1452 if (major != GSS_S_COMPLETE) { |
|
1453 gss_release_oid_set(minor_status, mech_set); |
|
1454 } |
|
1455 |
|
1456 return major; |
|
1457 } |
|
1458 |
|
1459 __declspec(dllexport) OM_uint32 |
|
1460 gss_inquire_names_for_mech(OM_uint32 *minor_status, |
|
1461 gss_const_OID mechanism, |
|
1462 gss_OID_set *name_types) |
|
1463 { |
|
1464 PP(">>>> Calling gss_inquire_names_for_mech..."); |
|
1465 CHECK_OID(mechanism) |
|
1466 |
|
1467 if (gss_create_empty_oid_set(minor_status, name_types)) { |
|
1468 return GSS_S_FAILURE; |
|
1469 } |
|
1470 if (gss_add_oid_set_member(minor_status, &USER_NAME_OID, name_types)) { |
|
1471 goto err; |
|
1472 } |
|
1473 if (gss_add_oid_set_member(minor_status, &HOST_SERVICE_NAME_OID, name_types)) { |
|
1474 goto err; |
|
1475 } |
|
1476 if (!is_same_oid(mechanism, &SPNEGO_OID)) { |
|
1477 if (gss_add_oid_set_member(minor_status, &EXPORT_NAME_OID, name_types)) { |
|
1478 goto err; |
|
1479 } |
|
1480 } |
|
1481 return GSS_S_COMPLETE; |
|
1482 err: |
|
1483 gss_release_oid_set(minor_status, name_types); |
|
1484 return GSS_S_FAILURE; |
|
1485 } |
|
1486 |
|
1487 __declspec(dllexport) OM_uint32 |
|
1488 gss_add_oid_set_member(OM_uint32 *minor_status, |
|
1489 gss_const_OID member_oid, |
|
1490 gss_OID_set *oid_set) |
|
1491 { |
|
1492 PP(">>>> Calling gss_add_oid_set_member..."); |
|
1493 CHECK_OID(member_oid) |
|
1494 CHECK_OUTPUT(oid_set) |
|
1495 |
|
1496 |
|
1497 int count = (int)(*oid_set)->count; |
|
1498 for (int i = 0; i < count; i++) { |
|
1499 if (is_same_oid(&(*oid_set)->elements[i], member_oid)) { |
|
1500 // already there |
|
1501 return GSS_S_COMPLETE; |
|
1502 } |
|
1503 } |
|
1504 gss_OID existing = (*oid_set)->elements; |
|
1505 gss_OID newcopy = new gss_OID_desc[count + 1]; |
|
1506 if (newcopy == NULL) { |
|
1507 return GSS_S_FAILURE; |
|
1508 } |
|
1509 if (existing) { |
|
1510 memcpy_s(newcopy, (count + 1) * sizeof(gss_OID_desc), |
|
1511 existing, count * sizeof(gss_OID_desc)); |
|
1512 } |
|
1513 newcopy[count].length = member_oid->length; |
|
1514 newcopy[count].elements = new char[member_oid->length]; |
|
1515 if (newcopy[count].elements == NULL) { |
|
1516 delete[] newcopy; |
|
1517 return GSS_S_FAILURE; |
|
1518 } |
|
1519 memcpy_s(newcopy[count].elements, member_oid->length, |
|
1520 member_oid->elements, member_oid->length); |
|
1521 (*oid_set)->elements = newcopy; |
|
1522 (*oid_set)->count++; |
|
1523 if (existing) { |
|
1524 delete[] existing; |
|
1525 } |
|
1526 |
|
1527 return GSS_S_COMPLETE; |
|
1528 } |
|
1529 |
|
1530 __declspec(dllexport) OM_uint32 |
|
1531 gss_display_status(OM_uint32 *minor_status, |
|
1532 OM_uint32 status_value, |
|
1533 int status_type, |
|
1534 gss_const_OID mech_type, |
|
1535 OM_uint32 *message_context, |
|
1536 gss_buffer_t status_string) |
|
1537 { |
|
1538 PP(">>>> Calling gss_display_status..."); |
|
1539 TCHAR msg[256]; |
|
1540 int len = FormatMessage( |
|
1541 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
|
1542 0, status_value, |
|
1543 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|
1544 msg, 256, 0); |
|
1545 if (len > 0) { |
|
1546 status_string->value = new char[len + 20]; |
|
1547 status_string->length = sprintf_s( |
|
1548 (LPSTR)status_string->value, len + 19, |
|
1549 "(%lx) %ls", status_value, msg); |
|
1550 } else { |
|
1551 status_string->value = new char[33]; |
|
1552 status_string->length = sprintf_s( |
|
1553 (LPSTR)status_string->value, 32, |
|
1554 "status is %lx", status_value); |
|
1555 } |
|
1556 if (status_string->length <= 0) { |
|
1557 gss_release_buffer(NULL, status_string); |
|
1558 status_string = GSS_C_NO_BUFFER; |
|
1559 return GSS_S_FAILURE; |
|
1560 } else { |
|
1561 return GSS_S_COMPLETE; |
|
1562 } |
|
1563 } |
|
1564 |
|
1565 __declspec(dllexport) OM_uint32 |
|
1566 gss_create_empty_oid_set(OM_uint32 *minor_status, |
|
1567 gss_OID_set *oid_set) |
|
1568 { |
|
1569 PP(">>>> Calling gss_create_empty_oid_set..."); |
|
1570 CHECK_OUTPUT(oid_set) |
|
1571 |
|
1572 if (*oid_set = new gss_OID_set_desc) { |
|
1573 memset(*oid_set, 0, sizeof(gss_OID_set_desc)); |
|
1574 return GSS_S_COMPLETE; |
|
1575 } |
|
1576 return GSS_S_FAILURE; |
|
1577 } |
|
1578 |
|
1579 __declspec(dllexport) OM_uint32 |
|
1580 gss_release_oid_set(OM_uint32 *minor_status, |
|
1581 gss_OID_set *set) |
|
1582 { |
|
1583 PP(">>>> Calling gss_release_oid_set..."); |
|
1584 if (set == NULL || *set == GSS_C_NO_OID_SET) { |
|
1585 return GSS_S_COMPLETE; |
|
1586 } |
|
1587 for (size_t i = 0; i < (*set)->count; i++) { |
|
1588 delete[] (*set)->elements[i].elements; |
|
1589 } |
|
1590 delete[] (*set)->elements; |
|
1591 delete *set; |
|
1592 *set = GSS_C_NO_OID_SET; |
|
1593 return GSS_S_COMPLETE; |
|
1594 } |
|
1595 |
|
1596 __declspec(dllexport) OM_uint32 |
|
1597 gss_release_buffer(OM_uint32 *minor_status, |
|
1598 gss_buffer_t buffer) |
|
1599 { |
|
1600 PP(">>>> Calling gss_release_buffer..."); |
|
1601 if (buffer == NULL || buffer == GSS_C_NO_BUFFER) { |
|
1602 return GSS_S_COMPLETE; |
|
1603 } |
|
1604 if (buffer->value) { |
|
1605 delete[] buffer->value; |
|
1606 buffer->value = NULL; |
|
1607 } |
|
1608 buffer->length = 0; |
|
1609 return GSS_S_COMPLETE; |
|
1610 } |
|
1611 |
|
1612 /* End implemented section */ |
|
1613 |
|
1614 #ifdef __cplusplus |
|
1615 } |
|
1616 #endif |