2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
8 #include "auth_negotiate.h"
19 static gss_OID_desc negotiate_oid_spnego
=
20 { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
21 static gss_OID_desc negotiate_oid_krb5
=
22 { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
24 static gss_OID negotiate_oids
[] =
25 { &negotiate_oid_spnego
, &negotiate_oid_krb5
, NULL
};
28 git_http_auth_context parent
;
29 unsigned configured
: 1,
33 gss_ctx_id_t gss_context
;
35 } http_auth_negotiate_context
;
37 static void negotiate_err_set(
38 OM_uint32 status_major
,
39 OM_uint32 status_minor
,
42 gss_buffer_desc buffer
= GSS_C_EMPTY_BUFFER
;
43 OM_uint32 status_display
, context
= 0;
45 if (gss_display_status(&status_display
, status_major
, GSS_C_GSS_CODE
,
46 GSS_C_NO_OID
, &context
, &buffer
) == GSS_S_COMPLETE
) {
47 git_error_set(GIT_ERROR_NET
, "%s: %.*s (%d.%d)",
48 message
, (int)buffer
.length
, (const char *)buffer
.value
,
49 status_major
, status_minor
);
50 gss_release_buffer(&status_minor
, &buffer
);
52 git_error_set(GIT_ERROR_NET
, "%s: unknown negotiate error (%d.%d)",
53 message
, status_major
, status_minor
);
57 static int negotiate_set_challenge(
58 git_http_auth_context
*c
,
59 const char *challenge
)
61 http_auth_negotiate_context
*ctx
= (http_auth_negotiate_context
*)c
;
63 assert(ctx
&& ctx
->configured
&& challenge
);
65 git__free(ctx
->challenge
);
67 ctx
->challenge
= git__strdup(challenge
);
68 GIT_ERROR_CHECK_ALLOC(ctx
->challenge
);
73 static int negotiate_next_token(
75 git_http_auth_context
*c
,
76 const char *header_name
,
79 http_auth_negotiate_context
*ctx
= (http_auth_negotiate_context
*)c
;
80 OM_uint32 status_major
, status_minor
;
81 gss_buffer_desc target_buffer
= GSS_C_EMPTY_BUFFER
,
82 input_token
= GSS_C_EMPTY_BUFFER
,
83 output_token
= GSS_C_EMPTY_BUFFER
;
84 gss_buffer_t input_token_ptr
= GSS_C_NO_BUFFER
;
85 git_buf input_buf
= GIT_BUF_INIT
;
86 gss_name_t server
= NULL
;
91 assert(buf
&& ctx
&& ctx
->configured
&& cred
&& cred
->credtype
== GIT_CREDTYPE_DEFAULT
);
96 target_buffer
.value
= (void *)ctx
->target
.ptr
;
97 target_buffer
.length
= ctx
->target
.size
;
99 status_major
= gss_import_name(&status_minor
, &target_buffer
,
100 GSS_C_NT_HOSTBASED_SERVICE
, &server
);
102 if (GSS_ERROR(status_major
)) {
103 negotiate_err_set(status_major
, status_minor
,
104 "Could not parse principal");
109 challenge_len
= ctx
->challenge
? strlen(ctx
->challenge
) : 0;
111 if (challenge_len
< 9) {
112 git_error_set(GIT_ERROR_NET
, "no negotiate challenge sent from server");
115 } else if (challenge_len
> 9) {
116 if (git_buf_decode_base64(&input_buf
,
117 ctx
->challenge
+ 10, challenge_len
- 10) < 0) {
118 git_error_set(GIT_ERROR_NET
, "invalid negotiate challenge from server");
123 input_token
.value
= input_buf
.ptr
;
124 input_token
.length
= input_buf
.size
;
125 input_token_ptr
= &input_token
;
126 } else if (ctx
->gss_context
!= GSS_C_NO_CONTEXT
) {
127 git_error_set(GIT_ERROR_NET
, "could not restart authentication");
132 mech
= &negotiate_oid_spnego
;
134 if (GSS_ERROR(status_major
= gss_init_sec_context(
140 GSS_C_DELEG_FLAG
| GSS_C_MUTUAL_FLAG
,
142 GSS_C_NO_CHANNEL_BINDINGS
,
148 negotiate_err_set(status_major
, status_minor
, "Negotiate failure");
153 /* This message merely told us auth was complete; we do not respond. */
154 if (status_major
== GSS_S_COMPLETE
) {
159 git_buf_printf(buf
, "%s: Negotiate ", header_name
);
160 git_buf_encode_base64(buf
, output_token
.value
, output_token
.length
);
161 git_buf_puts(buf
, "\r\n");
163 if (git_buf_oom(buf
))
167 gss_release_name(&status_minor
, &server
);
168 gss_release_buffer(&status_minor
, (gss_buffer_t
) &output_token
);
169 git_buf_dispose(&input_buf
);
173 static void negotiate_context_free(git_http_auth_context
*c
)
175 http_auth_negotiate_context
*ctx
= (http_auth_negotiate_context
*)c
;
176 OM_uint32 status_minor
;
178 if (ctx
->gss_context
!= GSS_C_NO_CONTEXT
) {
179 gss_delete_sec_context(
180 &status_minor
, &ctx
->gss_context
, GSS_C_NO_BUFFER
);
181 ctx
->gss_context
= GSS_C_NO_CONTEXT
;
184 git_buf_dispose(&ctx
->target
);
186 git__free(ctx
->challenge
);
195 static int negotiate_init_context(
196 http_auth_negotiate_context
*ctx
,
197 const gitno_connection_data
*connection_data
)
199 OM_uint32 status_major
, status_minor
;
201 gss_OID_set mechanism_list
;
204 /* Query supported mechanisms looking for SPNEGO) */
205 if (GSS_ERROR(status_major
=
206 gss_indicate_mechs(&status_minor
, &mechanism_list
))) {
207 negotiate_err_set(status_major
, status_minor
,
208 "could not query mechanisms");
212 if (mechanism_list
) {
213 for (oid
= negotiate_oids
; *oid
; oid
++) {
214 for (i
= 0; i
< mechanism_list
->count
; i
++) {
215 item
= &mechanism_list
->elements
[i
];
217 if (item
->length
== (*oid
)->length
&&
218 memcmp(item
->elements
, (*oid
)->elements
, item
->length
) == 0) {
230 gss_release_oid_set(&status_minor
, &mechanism_list
);
233 git_error_set(GIT_ERROR_NET
, "negotiate authentication is not supported");
237 git_buf_puts(&ctx
->target
, "HTTP@");
238 git_buf_puts(&ctx
->target
, connection_data
->host
);
240 if (git_buf_oom(&ctx
->target
))
243 ctx
->gss_context
= GSS_C_NO_CONTEXT
;
249 int git_http_auth_negotiate(
250 git_http_auth_context
**out
,
251 const gitno_connection_data
*connection_data
)
253 http_auth_negotiate_context
*ctx
;
257 ctx
= git__calloc(1, sizeof(http_auth_negotiate_context
));
258 GIT_ERROR_CHECK_ALLOC(ctx
);
260 if (negotiate_init_context(ctx
, connection_data
) < 0) {
265 ctx
->parent
.type
= GIT_AUTHTYPE_NEGOTIATE
;
266 ctx
->parent
.credtypes
= GIT_CREDTYPE_DEFAULT
;
267 ctx
->parent
.set_challenge
= negotiate_set_challenge
;
268 ctx
->parent
.next_token
= negotiate_next_token
;
269 ctx
->parent
.free
= negotiate_context_free
;
271 *out
= (git_http_auth_context
*)ctx
;
276 #endif /* GIT_GSSAPI */