]> git.proxmox.com Git - libgit2.git/blobdiff - src/transports/auth_negotiate.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / transports / auth_negotiate.c
index 7c868c9fd158f101eca5947a35467fb788624188..8a614b81a3a2f24f7d861ce14a2906ed39dc5ab4 100644 (file)
@@ -5,15 +5,21 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifdef GIT_GSSAPI
+#include "auth_negotiate.h"
+
+#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK)
 
 #include "git2.h"
-#include "common.h"
 #include "buffer.h"
 #include "auth.h"
+#include "git2/sys/credential.h"
 
+#ifdef GIT_GSSFRAMEWORK
+#import <GSS/GSS.h>
+#elif defined(GIT_GSSAPI)
 #include <gssapi.h>
 #include <krb5.h>
+#endif
 
 static gss_OID_desc negotiate_oid_spnego =
        { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
@@ -43,12 +49,12 @@ static void negotiate_err_set(
 
        if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
                GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
-               giterr_set(GITERR_NET, "%s: %.*s (%d.%d)",
+               git_error_set(GIT_ERROR_NET, "%s: %.*s (%d.%d)",
                        message, (int)buffer.length, (const char *)buffer.value,
                        status_major, status_minor);
                gss_release_buffer(&status_minor, &buffer);
        } else {
-               giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)",
+               git_error_set(GIT_ERROR_NET, "%s: unknown negotiate error (%d.%d)",
                        message, status_major, status_minor);
        }
 }
@@ -64,15 +70,31 @@ static int negotiate_set_challenge(
        git__free(ctx->challenge);
 
        ctx->challenge = git__strdup(challenge);
-       GITERR_CHECK_ALLOC(ctx->challenge);
+       GIT_ERROR_CHECK_ALLOC(ctx->challenge);
 
        return 0;
 }
 
+static void negotiate_context_dispose(http_auth_negotiate_context *ctx)
+{
+       OM_uint32 status_minor;
+
+       if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+               gss_delete_sec_context(
+                   &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
+               ctx->gss_context = GSS_C_NO_CONTEXT;
+       }
+
+       git_buf_dispose(&ctx->target);
+
+       git__free(ctx->challenge);
+       ctx->challenge = NULL;
+}
+
 static int negotiate_next_token(
        git_buf *buf,
        git_http_auth_context *c,
-       git_cred *cred)
+       git_credential *cred)
 {
        http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
        OM_uint32 status_major, status_minor;
@@ -86,7 +108,7 @@ static int negotiate_next_token(
        size_t challenge_len;
        int error = 0;
 
-       assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT);
+       assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDENTIAL_DEFAULT);
 
        if (ctx->complete)
                return 0;
@@ -99,21 +121,23 @@ static int negotiate_next_token(
 
        if (GSS_ERROR(status_major)) {
                negotiate_err_set(status_major, status_minor,
-                       "Could not parse principal");
+                       "could not parse principal");
                error = -1;
                goto done;
        }
 
        challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
 
-       if (challenge_len < 9) {
-               giterr_set(GITERR_NET, "no negotiate challenge sent from server");
+       if (challenge_len < 9 || memcmp(ctx->challenge, "Negotiate", 9) != 0) {
+               git_error_set(GIT_ERROR_NET, "server did not request negotiate");
                error = -1;
                goto done;
-       } else if (challenge_len > 9) {
+       }
+
+       if (challenge_len > 9) {
                if (git_buf_decode_base64(&input_buf,
                                ctx->challenge + 10, challenge_len - 10) < 0) {
-                       giterr_set(GITERR_NET, "invalid negotiate challenge from server");
+                       git_error_set(GIT_ERROR_NET, "invalid negotiate challenge from server");
                        error = -1;
                        goto done;
                }
@@ -122,14 +146,12 @@ static int negotiate_next_token(
                input_token.length = input_buf.size;
                input_token_ptr = &input_token;
        } else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
-               giterr_set(GITERR_NET, "could not restart authentication");
-               error = -1;
-               goto done;
+               negotiate_context_dispose(ctx);
        }
 
        mech = &negotiate_oid_spnego;
 
-       if (GSS_ERROR(status_major = gss_init_sec_context(
+       status_major = gss_init_sec_context(
                &status_minor,
                GSS_C_NO_CREDENTIAL,
                &ctx->gss_context,
@@ -142,21 +164,29 @@ static int negotiate_next_token(
                NULL,
                &output_token,
                NULL,
-               NULL))) {
-               negotiate_err_set(status_major, status_minor, "Negotiate failure");
+               NULL);
+
+       if (GSS_ERROR(status_major)) {
+               negotiate_err_set(status_major, status_minor, "negotiate failure");
                error = -1;
                goto done;
        }
 
        /* This message merely told us auth was complete; we do not respond. */
        if (status_major == GSS_S_COMPLETE) {
+               negotiate_context_dispose(ctx);
                ctx->complete = 1;
                goto done;
        }
 
-       git_buf_puts(buf, "Authorization: Negotiate ");
+       if (output_token.length == 0) {
+               git_error_set(GIT_ERROR_NET, "GSSAPI did not return token");
+               error = -1;
+               goto done;
+       }
+
+       git_buf_puts(buf, "Negotiate ");
        git_buf_encode_base64(buf, output_token.value, output_token.length);
-       git_buf_puts(buf, "\r\n");
 
        if (git_buf_oom(buf))
                error = -1;
@@ -164,24 +194,24 @@ static int negotiate_next_token(
 done:
        gss_release_name(&status_minor, &server);
        gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
-       git_buf_free(&input_buf);
+       git_buf_dispose(&input_buf);
        return error;
 }
 
-static void negotiate_context_free(git_http_auth_context *c)
+static int negotiate_is_complete(git_http_auth_context *c)
 {
        http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
-       OM_uint32 status_minor;
 
-       if (ctx->gss_context != GSS_C_NO_CONTEXT) {
-               gss_delete_sec_context(
-                       &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
-               ctx->gss_context = GSS_C_NO_CONTEXT;
-       }
+       assert(ctx);
 
-       git_buf_free(&ctx->target);
+       return (ctx->complete == 1);
+}
 
-       git__free(ctx->challenge);
+static void negotiate_context_free(git_http_auth_context *c)
+{
+       http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c;
+
+       negotiate_context_dispose(ctx);
 
        ctx->configured = 0;
        ctx->complete = 0;
@@ -192,7 +222,7 @@ static void negotiate_context_free(git_http_auth_context *c)
 
 static int negotiate_init_context(
        http_auth_negotiate_context *ctx,
-       const gitno_connection_data *connection_data)
+       const git_net_url *url)
 {
        OM_uint32 status_major, status_minor;
        gss_OID item, *oid;
@@ -200,8 +230,9 @@ static int negotiate_init_context(
        size_t i;
 
        /* Query supported mechanisms looking for SPNEGO) */
-       if (GSS_ERROR(status_major =
-               gss_indicate_mechs(&status_minor, &mechanism_list))) {
+       status_major = gss_indicate_mechs(&status_minor, &mechanism_list);
+
+       if (GSS_ERROR(status_major)) {
                negotiate_err_set(status_major, status_minor,
                        "could not query mechanisms");
                return -1;
@@ -228,12 +259,12 @@ static int negotiate_init_context(
        gss_release_oid_set(&status_minor, &mechanism_list);
 
        if (!ctx->oid) {
-               giterr_set(GITERR_NET, "negotiate authentication is not supported");
+               git_error_set(GIT_ERROR_NET, "negotiate authentication is not supported");
                return -1;
        }
 
        git_buf_puts(&ctx->target, "HTTP@");
-       git_buf_puts(&ctx->target, connection_data->host);
+       git_buf_puts(&ctx->target, url->host);
 
        if (git_buf_oom(&ctx->target))
                return -1;
@@ -246,24 +277,26 @@ static int negotiate_init_context(
 
 int git_http_auth_negotiate(
        git_http_auth_context **out,
-       const gitno_connection_data *connection_data)
+       const git_net_url *url)
 {
        http_auth_negotiate_context *ctx;
 
        *out = NULL;
 
        ctx = git__calloc(1, sizeof(http_auth_negotiate_context));
-       GITERR_CHECK_ALLOC(ctx);
+       GIT_ERROR_CHECK_ALLOC(ctx);
 
-       if (negotiate_init_context(ctx, connection_data) < 0) {
+       if (negotiate_init_context(ctx, url) < 0) {
                git__free(ctx);
                return -1;
        }
 
-       ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE;
-       ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT;
+       ctx->parent.type = GIT_HTTP_AUTH_NEGOTIATE;
+       ctx->parent.credtypes = GIT_CREDENTIAL_DEFAULT;
+       ctx->parent.connection_affinity = 1;
        ctx->parent.set_challenge = negotiate_set_challenge;
        ctx->parent.next_token = negotiate_next_token;
+       ctx->parent.is_complete = negotiate_is_complete;
        ctx->parent.free = negotiate_context_free;
 
        *out = (git_http_auth_context *)ctx;