2 * Unix interface for libkrb5/libgssapi_krb5
4 * Copyright 2017 Dmitry Timoshkov
5 * Copyright 2017 George Popoff
6 * Copyright 2008 Robert Shearman for CodeWeavers
7 * Copyright 2017,2021 Hans Leidekker for CodeWeavers
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #if defined(SONAME_LIBKRB5) && defined(SONAME_LIBGSSAPI_KRB5)
33 #include <sys/types.h>
36 #ifdef HAVE_KRB5_KRB5_H
37 # include <krb5/krb5.h>
39 #ifdef HAVE_GSSAPI_GSSAPI_H
40 # include <gssapi/gssapi.h>
42 #ifdef HAVE_GSSAPI_GSSAPI_EXT_H
43 # include <gssapi/gssapi_ext.h>
47 #define WIN32_NO_STATUS
56 #include "wine/debug.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(kerberos
);
60 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
62 static void *libkrb5_handle
;
64 #define MAKE_FUNCPTR(f) static typeof(f) * p_##f
65 MAKE_FUNCPTR( krb5_cc_close
);
66 MAKE_FUNCPTR( krb5_cc_default
);
67 MAKE_FUNCPTR( krb5_cc_end_seq_get
);
68 MAKE_FUNCPTR( krb5_cc_initialize
);
69 MAKE_FUNCPTR( krb5_cc_next_cred
);
70 MAKE_FUNCPTR( krb5_cc_start_seq_get
);
71 MAKE_FUNCPTR( krb5_cc_store_cred
);
72 MAKE_FUNCPTR( krb5_cccol_cursor_free
);
73 MAKE_FUNCPTR( krb5_cccol_cursor_new
);
74 MAKE_FUNCPTR( krb5_cccol_cursor_next
);
75 MAKE_FUNCPTR( krb5_decode_ticket
);
76 MAKE_FUNCPTR( krb5_free_context
);
77 MAKE_FUNCPTR( krb5_free_cred_contents
);
78 MAKE_FUNCPTR( krb5_free_principal
);
79 MAKE_FUNCPTR( krb5_free_ticket
);
80 MAKE_FUNCPTR( krb5_free_unparsed_name
);
81 MAKE_FUNCPTR( krb5_get_init_creds_opt_alloc
);
82 MAKE_FUNCPTR( krb5_get_init_creds_opt_free
);
83 MAKE_FUNCPTR( krb5_get_init_creds_opt_set_out_ccache
);
84 MAKE_FUNCPTR( krb5_get_init_creds_password
);
85 MAKE_FUNCPTR( krb5_init_context
);
86 MAKE_FUNCPTR( krb5_is_config_principal
);
87 MAKE_FUNCPTR( krb5_parse_name_flags
);
88 MAKE_FUNCPTR( krb5_unparse_name_flags
);
91 static BOOL
load_krb5(void)
93 if (!(libkrb5_handle
= dlopen( SONAME_LIBKRB5
, RTLD_NOW
)))
95 WARN_(winediag
)( "failed to load %s, Kerberos support will be disabled\n", SONAME_LIBKRB5
);
99 #define LOAD_FUNCPTR(f) \
100 if (!(p_##f = dlsym( libkrb5_handle, #f ))) \
102 ERR( "failed to load %s\n", #f ); \
106 LOAD_FUNCPTR( krb5_cc_close
)
107 LOAD_FUNCPTR( krb5_cc_default
)
108 LOAD_FUNCPTR( krb5_cc_end_seq_get
)
109 LOAD_FUNCPTR( krb5_cc_initialize
)
110 LOAD_FUNCPTR( krb5_cc_next_cred
)
111 LOAD_FUNCPTR( krb5_cc_start_seq_get
)
112 LOAD_FUNCPTR( krb5_cc_store_cred
)
113 LOAD_FUNCPTR( krb5_cccol_cursor_free
)
114 LOAD_FUNCPTR( krb5_cccol_cursor_new
)
115 LOAD_FUNCPTR( krb5_cccol_cursor_next
)
116 LOAD_FUNCPTR( krb5_decode_ticket
)
117 LOAD_FUNCPTR( krb5_free_context
)
118 LOAD_FUNCPTR( krb5_free_cred_contents
)
119 LOAD_FUNCPTR( krb5_free_principal
)
120 LOAD_FUNCPTR( krb5_free_ticket
)
121 LOAD_FUNCPTR( krb5_free_unparsed_name
)
122 LOAD_FUNCPTR( krb5_get_init_creds_opt_alloc
)
123 LOAD_FUNCPTR( krb5_get_init_creds_opt_free
)
124 LOAD_FUNCPTR( krb5_get_init_creds_opt_set_out_ccache
)
125 LOAD_FUNCPTR( krb5_get_init_creds_password
)
126 LOAD_FUNCPTR( krb5_init_context
)
127 LOAD_FUNCPTR( krb5_is_config_principal
)
128 LOAD_FUNCPTR( krb5_parse_name_flags
)
129 LOAD_FUNCPTR( krb5_unparse_name_flags
)
134 dlclose( libkrb5_handle
);
135 libkrb5_handle
= NULL
;
139 static void unload_krb5(void)
141 dlclose( libkrb5_handle
);
142 libkrb5_handle
= NULL
;
145 static NTSTATUS
krb5_error_to_status( krb5_error_code err
)
149 case 0: return STATUS_SUCCESS
;
150 default: return STATUS_UNSUCCESSFUL
; /* FIXME */
158 KERB_TICKET_CACHE_INFO
*tickets
;
161 static void utf8_to_wstr( UNICODE_STRING
*strW
, const char *src
)
163 ULONG dstlen
, srclen
= strlen( src
) + 1;
165 strW
->Buffer
= malloc( srclen
* sizeof(WCHAR
) );
166 RtlUTF8ToUnicodeN( strW
->Buffer
, srclen
* sizeof(WCHAR
), &dstlen
, src
, srclen
);
167 strW
->MaximumLength
= dstlen
;
168 strW
->Length
= dstlen
- sizeof(WCHAR
);
171 static NTSTATUS
copy_tickets_from_cache( krb5_context ctx
, krb5_ccache cache
, struct ticket_list
*list
)
174 krb5_cc_cursor cursor
;
178 char *name_with_realm
, *name_without_realm
, *realm_name
;
180 if ((err
= p_krb5_cc_start_seq_get( ctx
, cache
, &cursor
))) return krb5_error_to_status( err
);
183 if ((err
= p_krb5_cc_next_cred( ctx
, cache
, &cursor
, &creds
)))
185 if (err
== KRB5_CC_END
)
186 status
= STATUS_SUCCESS
;
188 status
= krb5_error_to_status( err
);
192 if (p_krb5_is_config_principal( ctx
, creds
.server
))
194 p_krb5_free_cred_contents( ctx
, &creds
);
198 if (list
->count
== list
->allocated
)
200 ULONG new_allocated
= max( 16, list
->allocated
* 2 );
201 KERB_TICKET_CACHE_INFO
*new_tickets
= realloc( list
->tickets
, sizeof(*new_tickets
) * new_allocated
);
204 p_krb5_free_cred_contents( ctx
, &creds
);
205 status
= STATUS_NO_MEMORY
;
208 list
->tickets
= new_tickets
;
209 list
->allocated
= new_allocated
;
212 if ((err
= p_krb5_unparse_name_flags( ctx
, creds
.server
, 0, &name_with_realm
)))
214 p_krb5_free_cred_contents( ctx
, &creds
);
215 status
= krb5_error_to_status( err
);
218 TRACE( "name_with_realm: %s\n", debugstr_a(name_with_realm
) );
220 if ((err
= p_krb5_unparse_name_flags( ctx
, creds
.server
, KRB5_PRINCIPAL_UNPARSE_NO_REALM
,
221 &name_without_realm
)))
223 p_krb5_free_unparsed_name( ctx
, name_with_realm
);
224 p_krb5_free_cred_contents( ctx
, &creds
);
225 status
= krb5_error_to_status( err
);
228 TRACE( "name_without_realm: %s\n", debugstr_a(name_without_realm
) );
230 utf8_to_wstr( &list
->tickets
[list
->count
].ServerName
, name_without_realm
);
232 if (!(realm_name
= strchr( name_with_realm
, '@' )))
234 ERR( "wrong name with realm %s\n", debugstr_a(name_with_realm
) );
235 realm_name
= name_with_realm
;
239 /* realm_name - now contains only realm! */
240 utf8_to_wstr( &list
->tickets
[list
->count
].RealmName
, realm_name
);
242 if (!creds
.times
.starttime
) creds
.times
.starttime
= creds
.times
.authtime
;
244 /* TODO: if krb5_is_config_principal = true */
246 /* note: store times as seconds, they will be converted to NT timestamps on the PE side */
247 list
->tickets
[list
->count
].StartTime
.QuadPart
= creds
.times
.starttime
;
248 list
->tickets
[list
->count
].EndTime
.QuadPart
= creds
.times
.endtime
;
249 list
->tickets
[list
->count
].RenewTime
.QuadPart
= creds
.times
.renew_till
;
250 list
->tickets
[list
->count
].TicketFlags
= creds
.ticket_flags
;
252 err
= p_krb5_decode_ticket( &creds
.ticket
, &ticket
);
253 p_krb5_free_unparsed_name( ctx
, name_with_realm
);
254 p_krb5_free_unparsed_name( ctx
, name_without_realm
);
255 p_krb5_free_cred_contents( ctx
, &creds
);
258 status
= krb5_error_to_status( err
);
262 list
->tickets
[list
->count
].EncryptionType
= ticket
->enc_part
.enctype
;
263 p_krb5_free_ticket( ctx
, ticket
);
267 p_krb5_cc_end_seq_get( ctx
, cache
, &cursor
);
271 static NTSTATUS
copy_tickets_to_client( struct ticket_list
*list
, KERB_QUERY_TKT_CACHE_RESPONSE
*resp
,
275 ULONG i
, size
= offsetof( KERB_QUERY_TKT_CACHE_RESPONSE
, Tickets
[list
->count
] );
277 for (i
= 0; i
< list
->count
; i
++)
279 size
+= list
->tickets
[i
].RealmName
.MaximumLength
;
280 size
+= list
->tickets
[i
].ServerName
.MaximumLength
;
282 if (!resp
|| size
> *out_size
)
285 return STATUS_BUFFER_TOO_SMALL
;
289 resp
->MessageType
= KerbQueryTicketCacheMessage
;
290 resp
->CountOfTickets
= list
->count
;
291 memcpy( resp
->Tickets
, list
->tickets
, list
->count
* sizeof(list
->tickets
[0]) );
292 client_str
= (char *)&resp
->Tickets
[list
->count
];
294 for (i
= 0; i
< list
->count
; i
++)
296 resp
->Tickets
[i
].RealmName
.Buffer
= (WCHAR
*)client_str
;
297 memcpy( client_str
, list
->tickets
[i
].RealmName
.Buffer
, list
->tickets
[i
].RealmName
.MaximumLength
);
298 client_str
+= list
->tickets
[i
].RealmName
.MaximumLength
;
299 resp
->Tickets
[i
].ServerName
.Buffer
= (WCHAR
*)client_str
;
300 memcpy( client_str
, list
->tickets
[i
].ServerName
.Buffer
, list
->tickets
[i
].ServerName
.MaximumLength
);
301 client_str
+= list
->tickets
[i
].ServerName
.MaximumLength
;
303 return STATUS_SUCCESS
;
306 static NTSTATUS
query_ticket_cache( void *args
)
308 struct query_ticket_cache_params
*params
= args
;
312 krb5_cccol_cursor cursor
= NULL
;
315 struct ticket_list list
= { 0 };
317 if ((err
= p_krb5_init_context( &ctx
))) return krb5_error_to_status( err
);
318 if ((err
= p_krb5_cccol_cursor_new( ctx
, &cursor
)))
320 status
= krb5_error_to_status( err
);
326 if ((err
= p_krb5_cccol_cursor_next( ctx
, cursor
, &cache
)))
328 status
= krb5_error_to_status( err
);
333 status
= copy_tickets_from_cache( ctx
, cache
, &list
);
334 p_krb5_cc_close( ctx
, cache
);
335 if (status
!= STATUS_SUCCESS
) goto done
;
339 if (cursor
) p_krb5_cccol_cursor_free( ctx
, &cursor
);
340 if (ctx
) p_krb5_free_context( ctx
);
342 if (status
== STATUS_SUCCESS
) status
= copy_tickets_to_client( &list
, params
->resp
, params
->out_size
);
344 for (i
= 0; i
< list
.count
; i
++)
346 free( list
.tickets
[i
].RealmName
.Buffer
);
347 free( list
.tickets
[i
].ServerName
.Buffer
);
352 static void *libgssapi_krb5_handle
;
354 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
355 MAKE_FUNCPTR( gss_accept_sec_context
);
356 MAKE_FUNCPTR( gss_acquire_cred
);
357 MAKE_FUNCPTR( gss_delete_sec_context
);
358 MAKE_FUNCPTR( gss_display_status
);
359 MAKE_FUNCPTR( gss_get_mic
);
360 MAKE_FUNCPTR( gss_import_name
);
361 MAKE_FUNCPTR( gss_init_sec_context
);
362 MAKE_FUNCPTR( gss_inquire_context
);
363 MAKE_FUNCPTR( gss_release_buffer
);
364 MAKE_FUNCPTR( gss_release_cred
);
365 MAKE_FUNCPTR( gss_release_iov_buffer
);
366 MAKE_FUNCPTR( gss_release_name
);
367 MAKE_FUNCPTR( gss_unwrap
);
368 MAKE_FUNCPTR( gss_unwrap_iov
);
369 MAKE_FUNCPTR( gss_verify_mic
);
370 MAKE_FUNCPTR( gss_wrap
);
371 MAKE_FUNCPTR( gss_wrap_iov
);
374 static BOOL
load_gssapi_krb5(void)
376 if (!(libgssapi_krb5_handle
= dlopen( SONAME_LIBGSSAPI_KRB5
, RTLD_NOW
)))
378 WARN_(winediag
)( "failed to load %s, Kerberos support will be disabled\n", SONAME_LIBGSSAPI_KRB5
);
382 #define LOAD_FUNCPTR(f) \
383 if (!(p##f = dlsym( libgssapi_krb5_handle, #f ))) \
385 ERR( "failed to load %s\n", #f ); \
389 LOAD_FUNCPTR( gss_accept_sec_context
)
390 LOAD_FUNCPTR( gss_acquire_cred
)
391 LOAD_FUNCPTR( gss_delete_sec_context
)
392 LOAD_FUNCPTR( gss_display_status
)
393 LOAD_FUNCPTR( gss_get_mic
)
394 LOAD_FUNCPTR( gss_import_name
)
395 LOAD_FUNCPTR( gss_init_sec_context
)
396 LOAD_FUNCPTR( gss_inquire_context
)
397 LOAD_FUNCPTR( gss_release_buffer
)
398 LOAD_FUNCPTR( gss_release_cred
)
399 LOAD_FUNCPTR( gss_release_iov_buffer
)
400 LOAD_FUNCPTR( gss_release_name
)
401 LOAD_FUNCPTR( gss_unwrap
)
402 LOAD_FUNCPTR( gss_unwrap_iov
)
403 LOAD_FUNCPTR( gss_verify_mic
)
404 LOAD_FUNCPTR( gss_wrap
)
405 LOAD_FUNCPTR( gss_wrap_iov
)
410 dlclose( libgssapi_krb5_handle
);
411 libgssapi_krb5_handle
= NULL
;
415 static BOOL
is_dce_style_context( gss_ctx_id_t ctx
)
417 OM_uint32 ret
, minor_status
, flags
;
418 ret
= pgss_inquire_context( &minor_status
, ctx
, NULL
, NULL
, NULL
, NULL
, &flags
, NULL
, NULL
);
419 return (ret
== GSS_S_COMPLETE
&& (flags
& GSS_C_DCE_STYLE
));
422 static int get_buffer_index( SecBufferDesc
*desc
, DWORD type
)
425 if (!desc
) return -1;
426 for (i
= 0; i
< desc
->cBuffers
; i
++)
428 if (desc
->pBuffers
[i
].BufferType
== type
) return i
;
433 static NTSTATUS
status_gss_to_sspi( OM_uint32 status
)
437 case GSS_S_COMPLETE
: return SEC_E_OK
;
438 case GSS_S_BAD_MECH
: return SEC_E_SECPKG_NOT_FOUND
;
439 case GSS_S_BAD_SIG
: return SEC_E_MESSAGE_ALTERED
;
440 case GSS_S_NO_CRED
: return SEC_E_NO_CREDENTIALS
;
441 case GSS_S_NO_CONTEXT
: return SEC_E_INVALID_HANDLE
;
442 case GSS_S_DEFECTIVE_TOKEN
: return SEC_E_INVALID_TOKEN
;
443 case GSS_S_DEFECTIVE_CREDENTIAL
: return SEC_E_NO_CREDENTIALS
;
444 case GSS_S_CREDENTIALS_EXPIRED
: return SEC_E_CONTEXT_EXPIRED
;
445 case GSS_S_CONTEXT_EXPIRED
: return SEC_E_CONTEXT_EXPIRED
;
446 case GSS_S_BAD_QOP
: return SEC_E_QOP_NOT_SUPPORTED
;
447 case GSS_S_CONTINUE_NEEDED
: return SEC_I_CONTINUE_NEEDED
;
448 case GSS_S_DUPLICATE_TOKEN
: return SEC_E_INVALID_TOKEN
;
449 case GSS_S_OLD_TOKEN
: return SEC_E_INVALID_TOKEN
;
450 case GSS_S_UNSEQ_TOKEN
: return SEC_E_OUT_OF_SEQUENCE
;
451 case GSS_S_GAP_TOKEN
: return SEC_E_OUT_OF_SEQUENCE
;
452 case GSS_S_FAILURE
: return SEC_E_INTERNAL_ERROR
;
455 FIXME( "couldn't convert status %#x to NTSTATUS\n", status
);
456 return SEC_E_INTERNAL_ERROR
;
460 static void trace_gss_status_ex( OM_uint32 code
, int type
)
462 OM_uint32 ret
, minor_status
;
464 OM_uint32 msg_ctx
= 0;
468 ret
= pgss_display_status( &minor_status
, code
, type
, GSS_C_NULL_OID
, &msg_ctx
, &buf
);
469 if (GSS_ERROR( ret
))
471 TRACE( "gss_display_status(%#x, %d) returned %#x minor status %#x\n", code
, type
, ret
, minor_status
);
474 TRACE( "GSS-API error: %#x: %s\n", code
, debugstr_an(buf
.value
, buf
.length
) );
475 pgss_release_buffer( &minor_status
, &buf
);
476 if (!msg_ctx
) return;
480 static void trace_gss_status( OM_uint32 major_status
, OM_uint32 minor_status
)
482 if (TRACE_ON(kerberos
))
484 trace_gss_status_ex( major_status
, GSS_C_GSS_CODE
);
485 trace_gss_status_ex( minor_status
, GSS_C_MECH_CODE
);
489 static inline gss_ctx_id_t
ctxhandle_sspi_to_gss( LSA_SEC_HANDLE handle
)
491 return (gss_ctx_id_t
)handle
;
494 static inline gss_cred_id_t
credhandle_sspi_to_gss( LSA_SEC_HANDLE handle
)
496 return (gss_cred_id_t
)handle
;
499 static inline void ctxhandle_gss_to_sspi( gss_ctx_id_t handle
, LSA_SEC_HANDLE
*ctx
)
501 *ctx
= (LSA_SEC_HANDLE
)handle
;
504 static inline void credhandle_gss_to_sspi( gss_cred_id_t handle
, LSA_SEC_HANDLE
*cred
)
506 *cred
= (LSA_SEC_HANDLE
)handle
;
509 static ULONG
flags_gss_to_asc_ret( ULONG flags
)
512 if (flags
& GSS_C_DELEG_FLAG
) ret
|= ASC_RET_DELEGATE
;
513 if (flags
& GSS_C_MUTUAL_FLAG
) ret
|= ASC_RET_MUTUAL_AUTH
;
514 if (flags
& GSS_C_REPLAY_FLAG
) ret
|= ASC_RET_REPLAY_DETECT
;
515 if (flags
& GSS_C_SEQUENCE_FLAG
) ret
|= ASC_RET_SEQUENCE_DETECT
;
516 if (flags
& GSS_C_CONF_FLAG
) ret
|= ASC_RET_CONFIDENTIALITY
;
517 if (flags
& GSS_C_INTEG_FLAG
) ret
|= ASC_RET_INTEGRITY
;
518 if (flags
& GSS_C_ANON_FLAG
) ret
|= ASC_RET_NULL_SESSION
;
519 if (flags
& GSS_C_DCE_STYLE
) ret
|= ASC_RET_USED_DCE_STYLE
;
520 if (flags
& GSS_C_IDENTIFY_FLAG
) ret
|= ASC_RET_IDENTIFY
;
524 static NTSTATUS
accept_context( void *args
)
526 struct accept_context_params
*params
= args
;
527 OM_uint32 ret
, minor_status
, ret_flags
= 0, expiry_time
;
528 gss_cred_id_t cred_handle
= credhandle_sspi_to_gss( params
->credential
);
529 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( params
->context
);
530 gss_buffer_desc input_token
, output_token
;
533 if (!params
->input
) input_token
.length
= 0;
536 if ((idx
= get_buffer_index( params
->input
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
537 input_token
.length
= params
->input
->pBuffers
[idx
].cbBuffer
;
538 input_token
.value
= params
->input
->pBuffers
[idx
].pvBuffer
;
541 if ((idx
= get_buffer_index( params
->output
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
542 output_token
.length
= 0;
543 output_token
.value
= NULL
;
545 ret
= pgss_accept_sec_context( &minor_status
, &ctx_handle
, cred_handle
, &input_token
, GSS_C_NO_CHANNEL_BINDINGS
,
546 NULL
, NULL
, &output_token
, &ret_flags
, &expiry_time
, NULL
);
547 TRACE( "gss_accept_sec_context returned %#x minor status %#x ret_flags %#x\n", ret
, minor_status
, ret_flags
);
548 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
549 if (ret
== GSS_S_COMPLETE
|| ret
== GSS_S_CONTINUE_NEEDED
)
551 if (output_token
.length
> params
->output
->pBuffers
[idx
].cbBuffer
) /* FIXME: check if larger buffer exists */
553 TRACE( "buffer too small %lu > %u\n",
554 (SIZE_T
)output_token
.length
, (unsigned int)params
->output
->pBuffers
[idx
].cbBuffer
);
555 pgss_release_buffer( &minor_status
, &output_token
);
556 pgss_delete_sec_context( &minor_status
, &ctx_handle
, GSS_C_NO_BUFFER
);
557 return SEC_E_BUFFER_TOO_SMALL
;
559 params
->output
->pBuffers
[idx
].cbBuffer
= output_token
.length
;
560 memcpy( params
->output
->pBuffers
[idx
].pvBuffer
, output_token
.value
, output_token
.length
);
561 pgss_release_buffer( &minor_status
, &output_token
);
563 ctxhandle_gss_to_sspi( ctx_handle
, params
->new_context
);
564 if (params
->context_attr
) *params
->context_attr
= flags_gss_to_asc_ret( ret_flags
);
565 *params
->expiry
= expiry_time
;
568 return status_gss_to_sspi( ret
);
571 static NTSTATUS
init_creds( const char *user_at_domain
, const char *password
)
574 krb5_principal principal
= NULL
;
575 krb5_get_init_creds_opt
*options
= NULL
;
576 krb5_ccache cache
= NULL
;
580 if (!user_at_domain
) return STATUS_SUCCESS
;
581 if ((err
= p_krb5_init_context( &ctx
))) return krb5_error_to_status( err
);
582 if ((err
= p_krb5_parse_name_flags( ctx
, user_at_domain
, 0, &principal
))) goto done
;
583 if ((err
= p_krb5_cc_default( ctx
, &cache
))) goto done
;
584 if ((err
= p_krb5_get_init_creds_opt_alloc( ctx
, &options
))) goto done
;
585 if ((err
= p_krb5_get_init_creds_opt_set_out_ccache( ctx
, options
, cache
))) goto done
;
586 if ((err
= p_krb5_get_init_creds_password( ctx
, &creds
, principal
, password
, 0, NULL
, 0, NULL
, 0 ))) goto done
;
587 if ((err
= p_krb5_cc_initialize( ctx
, cache
, principal
))) goto done
;
588 if ((err
= p_krb5_cc_store_cred( ctx
, cache
, &creds
))) goto done
;
590 TRACE( "success\n" );
591 p_krb5_free_cred_contents( ctx
, &creds
);
594 if (cache
) p_krb5_cc_close( ctx
, cache
);
595 if (principal
) p_krb5_free_principal( ctx
, principal
);
596 if (options
) p_krb5_get_init_creds_opt_free( ctx
, options
);
597 p_krb5_free_context( ctx
);
598 return krb5_error_to_status( err
);
601 static NTSTATUS
import_name( const char *src
, gss_name_t
*dst
)
603 OM_uint32 ret
, minor_status
;
606 buf
.length
= strlen( src
);
607 buf
.value
= (void *)src
;
608 ret
= pgss_import_name( &minor_status
, &buf
, GSS_C_NO_OID
, dst
);
609 TRACE( "gss_import_name returned %#x minor status %#x\n", ret
, minor_status
);
610 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
611 return status_gss_to_sspi( ret
);
614 static NTSTATUS
acquire_credentials_handle( void *args
)
616 struct acquire_credentials_handle_params
*params
= args
;
617 OM_uint32 ret
, minor_status
, expiry_time
;
618 gss_name_t name
= GSS_C_NO_NAME
;
619 gss_cred_usage_t cred_usage
;
620 gss_cred_id_t cred_handle
;
623 switch (params
->credential_use
)
625 case SECPKG_CRED_INBOUND
:
626 cred_usage
= GSS_C_ACCEPT
;
629 case SECPKG_CRED_OUTBOUND
:
630 if ((status
= init_creds( params
->username
, params
->password
)) != STATUS_SUCCESS
) return status
;
631 cred_usage
= GSS_C_INITIATE
;
635 FIXME( "SECPKG_CRED_BOTH not supported\n" );
636 return SEC_E_UNKNOWN_CREDENTIALS
;
639 if (params
->principal
&& (status
= import_name( params
->principal
, &name
))) return status
;
641 ret
= pgss_acquire_cred( &minor_status
, name
, GSS_C_INDEFINITE
, GSS_C_NULL_OID_SET
, cred_usage
, &cred_handle
,
642 NULL
, &expiry_time
);
643 TRACE( "gss_acquire_cred returned %#x minor status %#x\n", ret
, minor_status
);
644 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
645 if (ret
== GSS_S_COMPLETE
)
647 credhandle_gss_to_sspi( cred_handle
, params
->credential
);
648 *params
->expiry
= expiry_time
;
651 if (name
!= GSS_C_NO_NAME
) pgss_release_name( &minor_status
, &name
);
652 return status_gss_to_sspi( ret
);
655 static NTSTATUS
delete_context( void *args
)
657 OM_uint32 ret
, minor_status
;
658 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( (LSA_SEC_HANDLE
)args
);
660 ret
= pgss_delete_sec_context( &minor_status
, &ctx_handle
, GSS_C_NO_BUFFER
);
661 TRACE( "gss_delete_sec_context returned %#x minor status %#x\n", ret
, minor_status
);
662 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
663 return status_gss_to_sspi( ret
);
666 static NTSTATUS
free_credentials_handle( void *args
)
668 OM_uint32 ret
, minor_status
;
669 gss_cred_id_t cred
= credhandle_sspi_to_gss( (LSA_SEC_HANDLE
)args
);
671 ret
= pgss_release_cred( &minor_status
, &cred
);
672 TRACE( "gss_release_cred returned %#x minor status %#x\n", ret
, minor_status
);
673 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
674 return status_gss_to_sspi( ret
);
677 static ULONG
flags_isc_req_to_gss( ULONG flags
)
680 if (flags
& ISC_REQ_DELEGATE
) ret
|= GSS_C_DELEG_FLAG
;
681 if (flags
& ISC_REQ_MUTUAL_AUTH
) ret
|= GSS_C_MUTUAL_FLAG
;
682 if (flags
& ISC_REQ_REPLAY_DETECT
) ret
|= GSS_C_REPLAY_FLAG
;
683 if (flags
& ISC_REQ_SEQUENCE_DETECT
) ret
|= GSS_C_SEQUENCE_FLAG
;
684 if (flags
& ISC_REQ_CONFIDENTIALITY
) ret
|= GSS_C_CONF_FLAG
;
685 if (flags
& ISC_REQ_INTEGRITY
) ret
|= GSS_C_INTEG_FLAG
;
686 if (flags
& ISC_REQ_NULL_SESSION
) ret
|= GSS_C_ANON_FLAG
;
687 if (flags
& ISC_REQ_USE_DCE_STYLE
) ret
|= GSS_C_DCE_STYLE
;
688 if (flags
& ISC_REQ_IDENTIFY
) ret
|= GSS_C_IDENTIFY_FLAG
;
692 static ULONG
flags_gss_to_isc_ret( ULONG flags
)
695 if (flags
& GSS_C_DELEG_FLAG
) ret
|= ISC_RET_DELEGATE
;
696 if (flags
& GSS_C_MUTUAL_FLAG
) ret
|= ISC_RET_MUTUAL_AUTH
;
697 if (flags
& GSS_C_REPLAY_FLAG
) ret
|= ISC_RET_REPLAY_DETECT
;
698 if (flags
& GSS_C_SEQUENCE_FLAG
) ret
|= ISC_RET_SEQUENCE_DETECT
;
699 if (flags
& GSS_C_CONF_FLAG
) ret
|= ISC_RET_CONFIDENTIALITY
;
700 if (flags
& GSS_C_INTEG_FLAG
) ret
|= ISC_RET_INTEGRITY
;
701 if (flags
& GSS_C_ANON_FLAG
) ret
|= ISC_RET_NULL_SESSION
;
702 if (flags
& GSS_C_DCE_STYLE
) ret
|= ISC_RET_USED_DCE_STYLE
;
703 if (flags
& GSS_C_IDENTIFY_FLAG
) ret
|= ISC_RET_IDENTIFY
;
707 static NTSTATUS
initialize_context( void *args
)
709 struct initialize_context_params
*params
= args
;
710 OM_uint32 ret
, minor_status
, ret_flags
= 0, expiry_time
, req_flags
= flags_isc_req_to_gss( params
->context_req
);
711 gss_cred_id_t cred_handle
= credhandle_sspi_to_gss( params
->credential
);
712 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( params
->context
);
713 gss_buffer_desc input_token
, output_token
;
714 gss_name_t target
= GSS_C_NO_NAME
;
718 if ((idx
= get_buffer_index( params
->input
, SECBUFFER_TOKEN
)) == -1) input_token
.length
= 0;
721 input_token
.length
= params
->input
->pBuffers
[idx
].cbBuffer
;
722 input_token
.value
= params
->input
->pBuffers
[idx
].pvBuffer
;
725 if ((idx
= get_buffer_index( params
->output
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
726 output_token
.length
= 0;
727 output_token
.value
= NULL
;
729 if (params
->target_name
&& (status
= import_name( params
->target_name
, &target
))) return status
;
731 ret
= pgss_init_sec_context( &minor_status
, cred_handle
, &ctx_handle
, target
, GSS_C_NO_OID
, req_flags
, 0,
732 GSS_C_NO_CHANNEL_BINDINGS
, &input_token
, NULL
, &output_token
, &ret_flags
,
734 TRACE( "gss_init_sec_context returned %#x minor status %#x ret_flags %#x\n", ret
, minor_status
, ret_flags
);
735 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
736 if (ret
== GSS_S_COMPLETE
|| ret
== GSS_S_CONTINUE_NEEDED
)
738 if (output_token
.length
> params
->output
->pBuffers
[idx
].cbBuffer
) /* FIXME: check if larger buffer exists */
740 TRACE( "buffer too small %lu > %u\n",
741 (SIZE_T
)output_token
.length
, (unsigned int)params
->output
->pBuffers
[idx
].cbBuffer
);
742 pgss_release_buffer( &minor_status
, &output_token
);
743 pgss_delete_sec_context( &minor_status
, &ctx_handle
, GSS_C_NO_BUFFER
);
744 return SEC_E_INCOMPLETE_MESSAGE
;
746 params
->output
->pBuffers
[idx
].cbBuffer
= output_token
.length
;
747 memcpy( params
->output
->pBuffers
[idx
].pvBuffer
, output_token
.value
, output_token
.length
);
748 pgss_release_buffer( &minor_status
, &output_token
);
750 ctxhandle_gss_to_sspi( ctx_handle
, params
->new_context
);
751 if (params
->context_attr
) *params
->context_attr
= flags_gss_to_isc_ret( ret_flags
);
752 *params
->expiry
= expiry_time
;
755 if (target
!= GSS_C_NO_NAME
) pgss_release_name( &minor_status
, &target
);
756 return status_gss_to_sspi( ret
);
759 static NTSTATUS
make_signature( void *args
)
761 struct make_signature_params
*params
= args
;
762 SecBufferDesc
*msg
= params
->msg
;
763 OM_uint32 ret
, minor_status
;
764 gss_buffer_desc data_buffer
, token_buffer
;
765 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( params
->context
);
766 int data_idx
, token_idx
;
768 /* FIXME: multiple data buffers, read-only buffers */
769 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
770 data_buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
771 data_buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
773 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
774 token_buffer
.length
= 0;
775 token_buffer
.value
= NULL
;
777 ret
= pgss_get_mic( &minor_status
, ctx_handle
, GSS_C_QOP_DEFAULT
, &data_buffer
, &token_buffer
);
778 TRACE( "gss_get_mic returned %#x minor status %#x\n", ret
, minor_status
);
779 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
780 if (ret
== GSS_S_COMPLETE
)
782 memcpy( msg
->pBuffers
[token_idx
].pvBuffer
, token_buffer
.value
, token_buffer
.length
);
783 msg
->pBuffers
[token_idx
].cbBuffer
= token_buffer
.length
;
784 pgss_release_buffer( &minor_status
, &token_buffer
);
787 return status_gss_to_sspi( ret
);
790 #define KERBEROS_MAX_SIGNATURE 37
791 #define KERBEROS_SECURITY_TRAILER 49
792 #define KERBEROS_MAX_SIGNATURE_DCE 28
793 #define KERBEROS_SECURITY_TRAILER_DCE 76
795 static NTSTATUS
query_context_attributes( void *args
)
797 struct query_context_attributes_params
*params
= args
;
798 switch (params
->attr
)
800 case SECPKG_ATTR_SIZES
:
802 SecPkgContext_Sizes
*sizes
= (SecPkgContext_Sizes
*)params
->buf
;
803 ULONG size_max_signature
, size_security_trailer
;
804 gss_ctx_id_t ctx
= ctxhandle_sspi_to_gss( params
->context
);
806 if (is_dce_style_context( ctx
))
808 size_max_signature
= KERBEROS_MAX_SIGNATURE_DCE
;
809 size_security_trailer
= KERBEROS_SECURITY_TRAILER_DCE
;
813 size_max_signature
= KERBEROS_MAX_SIGNATURE
;
814 size_security_trailer
= KERBEROS_SECURITY_TRAILER
;
816 sizes
->cbMaxToken
= KERBEROS_MAX_BUF
;
817 sizes
->cbMaxSignature
= size_max_signature
;
818 sizes
->cbBlockSize
= 1;
819 sizes
->cbSecurityTrailer
= size_security_trailer
;
823 FIXME( "unhandled attribute %u\n", params
->attr
);
827 return SEC_E_UNSUPPORTED_FUNCTION
;
830 static NTSTATUS
seal_message_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, unsigned qop
)
832 gss_iov_buffer_desc iov
[4];
833 OM_uint32 ret
, minor_status
;
834 int token_idx
, data_idx
, conf_flag
, conf_state
;
837 conf_flag
= 1; /* confidentiality + integrity */
838 else if (qop
== SECQOP_WRAP_NO_ENCRYPT
)
839 conf_flag
= 0; /* only integrity */
842 FIXME( "QOP %#x not supported\n", qop
);
843 return SEC_E_UNSUPPORTED_FUNCTION
;
846 /* FIXME: multiple data buffers, read-only buffers */
847 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
848 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
850 iov
[0].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
851 iov
[0].buffer
.length
= 0;
852 iov
[0].buffer
.value
= NULL
;
854 iov
[1].type
= GSS_IOV_BUFFER_TYPE_DATA
;
855 iov
[1].buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
856 iov
[1].buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
858 iov
[2].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
859 iov
[2].buffer
.length
= 0;
860 iov
[2].buffer
.value
= NULL
;
862 iov
[3].type
= GSS_IOV_BUFFER_TYPE_HEADER
| GSS_IOV_BUFFER_FLAG_ALLOCATE
;
863 iov
[3].buffer
.length
= 0;
864 iov
[3].buffer
.value
= NULL
;
866 ret
= pgss_wrap_iov( &minor_status
, ctx
, conf_flag
, GSS_C_QOP_DEFAULT
, &conf_state
, iov
, 4 );
867 TRACE( "gss_wrap_iov returned %#x minor status %#x\n", ret
, minor_status
);
868 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
869 if (ret
== GSS_S_COMPLETE
)
871 memcpy( msg
->pBuffers
[token_idx
].pvBuffer
, iov
[3].buffer
.value
, iov
[3].buffer
.length
);
872 msg
->pBuffers
[token_idx
].cbBuffer
= iov
[3].buffer
.length
;
873 pgss_release_iov_buffer( &minor_status
, iov
, 4 );
876 return status_gss_to_sspi( ret
);
879 static NTSTATUS
seal_message_no_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, unsigned qop
)
881 gss_buffer_desc input
, output
;
882 OM_uint32 ret
, minor_status
;
883 int token_idx
, data_idx
, conf_flag
, conf_state
;
886 conf_flag
= 1; /* confidentiality + integrity */
887 else if (qop
== SECQOP_WRAP_NO_ENCRYPT
)
888 conf_flag
= 0; /* only integrity */
891 FIXME( "QOP %#x not supported\n", qop
);
892 return SEC_E_UNSUPPORTED_FUNCTION
;
895 /* FIXME: multiple data buffers, read-only buffers */
896 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
897 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
899 input
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
900 input
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
902 ret
= pgss_wrap( &minor_status
, ctx
, conf_flag
, GSS_C_QOP_DEFAULT
, &input
, &conf_state
, &output
);
903 TRACE( "gss_wrap returned %#x minor status %#x\n", ret
, minor_status
);
904 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
905 if (ret
== GSS_S_COMPLETE
)
907 unsigned len_data
= msg
->pBuffers
[data_idx
].cbBuffer
, len_token
= msg
->pBuffers
[token_idx
].cbBuffer
;
908 if (len_token
< output
.length
- len_data
)
910 TRACE( "buffer too small %lu > %u\n", (SIZE_T
)output
.length
- len_data
, len_token
);
911 pgss_release_buffer( &minor_status
, &output
);
912 return SEC_E_BUFFER_TOO_SMALL
;
914 memcpy( msg
->pBuffers
[data_idx
].pvBuffer
, output
.value
, len_data
);
915 memcpy( msg
->pBuffers
[token_idx
].pvBuffer
, (char *)output
.value
+ len_data
, output
.length
- len_data
);
916 msg
->pBuffers
[token_idx
].cbBuffer
= output
.length
- len_data
;
917 pgss_release_buffer( &minor_status
, &output
);
920 return status_gss_to_sspi( ret
);
923 static NTSTATUS
seal_message( void *args
)
925 struct seal_message_params
*params
= args
;
926 gss_ctx_id_t ctx
= ctxhandle_sspi_to_gss( params
->context
);
928 if (is_dce_style_context( ctx
)) return seal_message_vector( ctx
, params
->msg
, params
->qop
);
929 return seal_message_no_vector( ctx
, params
->msg
, params
->qop
);
932 static NTSTATUS
unseal_message_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, ULONG
*qop
)
934 gss_iov_buffer_desc iov
[4];
935 OM_uint32 ret
, minor_status
;
936 int token_idx
, data_idx
, conf_state
;
938 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
939 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
941 iov
[0].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
;
942 iov
[0].buffer
.length
= 0;
943 iov
[0].buffer
.value
= NULL
;
945 iov
[1].type
= GSS_IOV_BUFFER_TYPE_DATA
;
946 iov
[1].buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
947 iov
[1].buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
949 iov
[2].type
= GSS_IOV_BUFFER_TYPE_SIGN_ONLY
;
950 iov
[2].buffer
.length
= 0;
951 iov
[2].buffer
.value
= NULL
;
953 iov
[3].type
= GSS_IOV_BUFFER_TYPE_HEADER
;
954 iov
[3].buffer
.length
= msg
->pBuffers
[token_idx
].cbBuffer
;
955 iov
[3].buffer
.value
= msg
->pBuffers
[token_idx
].pvBuffer
;
957 ret
= pgss_unwrap_iov( &minor_status
, ctx
, &conf_state
, NULL
, iov
, 4 );
958 TRACE( "gss_unwrap_iov returned %#x minor status %#x\n", ret
, minor_status
);
959 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
960 if (ret
== GSS_S_COMPLETE
&& qop
)
962 *qop
= (conf_state
? 0 : SECQOP_WRAP_NO_ENCRYPT
);
964 return status_gss_to_sspi( ret
);
967 static NTSTATUS
unseal_message_no_vector( gss_ctx_id_t ctx
, SecBufferDesc
*msg
, ULONG
*qop
)
969 gss_buffer_desc input
, output
;
970 OM_uint32 ret
, minor_status
;
971 int token_idx
, data_idx
, conf_state
;
972 DWORD len_data
, len_token
;
974 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
975 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
977 len_data
= msg
->pBuffers
[data_idx
].cbBuffer
;
978 len_token
= msg
->pBuffers
[token_idx
].cbBuffer
;
980 input
.length
= len_data
+ len_token
;
981 if (!(input
.value
= malloc( input
.length
))) return SEC_E_INSUFFICIENT_MEMORY
;
982 memcpy( input
.value
, msg
->pBuffers
[data_idx
].pvBuffer
, len_data
);
983 memcpy( (char *)input
.value
+ len_data
, msg
->pBuffers
[token_idx
].pvBuffer
, len_token
);
985 ret
= pgss_unwrap( &minor_status
, ctx
, &input
, &output
, &conf_state
, NULL
);
987 TRACE( "gss_unwrap returned %#x minor status %#x\n", ret
, minor_status
);
988 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
989 if (ret
== GSS_S_COMPLETE
)
991 if (qop
) *qop
= (conf_state
? 0 : SECQOP_WRAP_NO_ENCRYPT
);
992 memcpy( msg
->pBuffers
[data_idx
].pvBuffer
, output
.value
, len_data
);
993 pgss_release_buffer( &minor_status
, &output
);
996 return status_gss_to_sspi( ret
);
999 static NTSTATUS
unseal_message( void *args
)
1001 struct unseal_message_params
*params
= args
;
1002 gss_ctx_id_t ctx
= ctxhandle_sspi_to_gss( params
->context
);
1004 if (is_dce_style_context( ctx
)) return unseal_message_vector( ctx
, params
->msg
, params
->qop
);
1005 return unseal_message_no_vector( ctx
, params
->msg
, params
->qop
);
1008 static NTSTATUS
verify_signature( void *args
)
1010 struct verify_signature_params
*params
= args
;
1011 SecBufferDesc
*msg
= params
->msg
;
1012 OM_uint32 ret
, minor_status
;
1013 gss_buffer_desc data_buffer
, token_buffer
;
1014 gss_ctx_id_t ctx_handle
= ctxhandle_sspi_to_gss( params
->context
);
1015 int data_idx
, token_idx
;
1017 if ((data_idx
= get_buffer_index( msg
, SECBUFFER_DATA
)) == -1) return SEC_E_INVALID_TOKEN
;
1018 data_buffer
.length
= msg
->pBuffers
[data_idx
].cbBuffer
;
1019 data_buffer
.value
= msg
->pBuffers
[data_idx
].pvBuffer
;
1021 if ((token_idx
= get_buffer_index( msg
, SECBUFFER_TOKEN
)) == -1) return SEC_E_INVALID_TOKEN
;
1022 token_buffer
.length
= msg
->pBuffers
[token_idx
].cbBuffer
;
1023 token_buffer
.value
= msg
->pBuffers
[token_idx
].pvBuffer
;
1025 ret
= pgss_verify_mic( &minor_status
, ctx_handle
, &data_buffer
, &token_buffer
, NULL
);
1026 TRACE( "gss_verify_mic returned %#x minor status %#x\n", ret
, minor_status
);
1027 if (GSS_ERROR( ret
)) trace_gss_status( ret
, minor_status
);
1028 if (ret
== GSS_S_COMPLETE
&& params
->qop
) *params
->qop
= 0;
1030 return status_gss_to_sspi( ret
);
1033 static NTSTATUS
process_attach( void *args
)
1035 if (load_krb5() && load_gssapi_krb5()) return STATUS_SUCCESS
;
1036 if (libkrb5_handle
) unload_krb5();
1037 return STATUS_DLL_NOT_FOUND
;
1040 unixlib_entry_t __wine_unix_call_funcs
[] =
1044 acquire_credentials_handle
,
1046 free_credentials_handle
,
1049 query_context_attributes
,
1056 #endif /* defined(SONAME_LIBKRB5) && defined(SONAME_LIBGSSAPI_KRB5) */