source src/transports/auth_ntlm.c
| Line | Flow | Count | Block(s) | Source | 
|---|---|---|---|---|
| 1 | - | /* | ||
| 2 | - | * Copyright (C) the libgit2 contributors. All rights reserved. | ||
| 3 | - | * | ||
| 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. | ||
| 6 | - | */ | ||
| 7 | - | |||
| 8 | - | #include "git2.h" | ||
| 9 | - | #include "common.h" | ||
| 10 | - | #include "buffer.h" | ||
| 11 | - | #include "auth.h" | ||
| 12 | - | #include "auth_ntlm.h" | ||
| 13 | - | #include "git2/sys/credential.h" | ||
| 14 | - | |||
| 15 | - | #ifdef GIT_NTLM | ||
| 16 | - | |||
| 17 | - | #include "ntlm.h" | ||
| 18 | - | |||
| 19 | - | typedef struct { | ||
| 20 | - | git_http_auth_context parent; | ||
| 21 | - | ntlm_client *ntlm; | ||
| 22 | - | char *challenge; | ||
| 23 | - | bool complete; | ||
| 24 | - | } http_auth_ntlm_context; | ||
| 25 | - | |||
| 26 |  | ##### | 2 | static int ntlm_set_challenge( | 
| 27 | - | git_http_auth_context *c, | ||
| 28 | - | const char *challenge) | ||
| 29 | - | { | ||
| 30 | ##### | 2 | http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; | |
| 31 | - | |||
| 32 | ##### | 2-4 | assert(ctx && challenge); | |
| 33 | - | |||
| 34 | ##### | 5 | git__free(ctx->challenge); | |
| 35 | - | |||
| 36 | ##### | 6 | ctx->challenge = git__strdup(challenge); | |
| 37 | ##### | 7,8 | GIT_ERROR_CHECK_ALLOC(ctx->challenge); | |
| 38 | - | |||
| 39 | ##### | 9 | return 0; | |
| 40 | - | } | ||
| 41 | - | |||
| 42 |  | ##### | 2 | static int ntlm_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred) | 
| 43 | - | { | ||
| 44 | - | git_credential_userpass_plaintext *cred; | ||
| 45 | - | const char *sep, *username; | ||
| 46 | ##### | 2 | char *domain = NULL, *domainuser = NULL; | |
| 47 | ##### | 2 | int error = 0; | |
| 48 | - | |||
| 49 | ##### | 2,3 | assert(_cred->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT); | |
| 50 | ##### | 4 | cred = (git_credential_userpass_plaintext *)_cred; | |
| 51 | - | |||
| 52 | ##### | 4 | if ((sep = strchr(cred->username, '\\')) != NULL) { | |
| 53 | ##### | 5 | domain = git__strndup(cred->username, (sep - cred->username)); | |
| 54 | ##### | 6,7 | GIT_ERROR_CHECK_ALLOC(domain); | |
| 55 | - | |||
| 56 | ##### | 8 | domainuser = git__strdup(sep + 1); | |
| 57 | ##### | 9,10 | GIT_ERROR_CHECK_ALLOC(domainuser); | |
| 58 | - | |||
| 59 | ##### | 11 | username = domainuser; | |
| 60 | - | } else { | ||
| 61 | ##### | 12 | username = cred->username; | |
| 62 | - | } | ||
| 63 | - | |||
| 64 | ##### | 13,14 | if (ntlm_client_set_credentials(ctx->ntlm, | |
| 65 | ##### | 13 | username, domain, cred->password) < 0) { | |
| 66 | ##### | 15,16 | git_error_set(GIT_ERROR_NET, "could not set credentials: %s", | |
| 67 | - | ntlm_client_errmsg(ctx->ntlm)); | ||
| 68 | ##### | 17 | error = -1; | |
| 69 | ##### | 17 | goto done; | |
| 70 | - | } | ||
| 71 | - | |||
| 72 | - | done: | ||
| 73 | ##### | 18 | git__free(domain); | |
| 74 | ##### | 19 | git__free(domainuser); | |
| 75 | ##### | 20 | return error; | |
| 76 | - | } | ||
| 77 | - | |||
| 78 |  | ##### | 2 | static int ntlm_next_token( | 
| 79 | - | git_buf *buf, | ||
| 80 | - | git_http_auth_context *c, | ||
| 81 | - | git_credential *cred) | ||
| 82 | - | { | ||
| 83 | ##### | 2 | http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; | |
| 84 | ##### | 2 | git_buf input_buf = GIT_BUF_INIT; | |
| 85 | - | const unsigned char *msg; | ||
| 86 | - | size_t challenge_len, msg_len; | ||
| 87 | ##### | 2 | int error = -1; | |
| 88 | - | |||
| 89 | ##### | 2-5 | assert(buf && ctx && ctx->ntlm); | |
| 90 | - | |||
| 91 | ##### | 6-8 | challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0; | |
| 92 | - | |||
| 93 | ##### | 9 | if (ctx->complete) | |
| 94 | ##### | 10 | ntlm_client_reset(ctx->ntlm); | |
| 95 | - | |||
| 96 | - | /* | ||
| 97 | - | * Set us complete now since it's the default case; the one | ||
| 98 | - | * incomplete case (successfully created a client request) | ||
| 99 | - | * will explicitly set that it requires a second step. | ||
| 100 | - | */ | ||
| 101 | ##### | 11 | ctx->complete = true; | |
| 102 | - | |||
| 103 | ##### | 11-13 | if (cred && ntlm_set_credentials(ctx, cred) != 0) | |
| 104 | ##### | 14 | goto done; | |
| 105 | - | |||
| 106 | ##### | 15 | if (challenge_len < 4) { | |
| 107 | ##### | 16 | git_error_set(GIT_ERROR_NET, "no ntlm challenge sent from server"); | |
| 108 | ##### | 17 | goto done; | |
| 109 | ##### | 18 | } else if (challenge_len == 4) { | |
| 110 | ##### | 19 | if (memcmp(ctx->challenge, "NTLM", 4) != 0) { | |
| 111 | ##### | 20 | git_error_set(GIT_ERROR_NET, "server did not request NTLM"); | |
| 112 | ##### | 21 | goto done; | |
| 113 | - | } | ||
| 114 | - | |||
| 115 | ##### | 22,23 | if (ntlm_client_negotiate(&msg, &msg_len, ctx->ntlm) != 0) { | |
| 116 | ##### | 24,25 | git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s", | |
| 117 | - | ntlm_client_errmsg(ctx->ntlm)); | ||
| 118 | ##### | 26 | goto done; | |
| 119 | - | } | ||
| 120 | - | |||
| 121 | ##### | 27 | ctx->complete = false; | |
| 122 | - | } else { | ||
| 123 | ##### | 28 | if (memcmp(ctx->challenge, "NTLM ", 5) != 0) { | |
| 124 | ##### | 29 | git_error_set(GIT_ERROR_NET, "challenge from server was not NTLM"); | |
| 125 | ##### | 30 | goto done; | |
| 126 | - | } | ||
| 127 | - | |||
| 128 | ##### | 31,31,32 | if (git_buf_decode_base64(&input_buf, | |
| 129 | ##### | 31 | ctx->challenge + 5, challenge_len - 5) < 0) { | |
| 130 | ##### | 33 | git_error_set(GIT_ERROR_NET, "invalid NTLM challenge from server"); | |
| 131 | ##### | 34 | goto done; | |
| 132 | - | } | ||
| 133 | - | |||
| 134 | ##### | 35,35,36 | if (ntlm_client_set_challenge(ctx->ntlm, | |
| 135 | ##### | 35 | (const unsigned char *)input_buf.ptr, input_buf.size) != 0) { | |
| 136 | ##### | 37,38 | git_error_set(GIT_ERROR_NET, "ntlm challenge failed: %s", | |
| 137 | - | ntlm_client_errmsg(ctx->ntlm)); | ||
| 138 | ##### | 39 | goto done; | |
| 139 | - | } | ||
| 140 | - | |||
| 141 | ##### | 40,41 | if (ntlm_client_response(&msg, &msg_len, ctx->ntlm) != 0) { | |
| 142 | ##### | 42,43 | git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s", | |
| 143 | - | ntlm_client_errmsg(ctx->ntlm)); | ||
| 144 | ##### | 44 | goto done; | |
| 145 | - | } | ||
| 146 | - | } | ||
| 147 | - | |||
| 148 | ##### | 45 | git_buf_puts(buf, "NTLM "); | |
| 149 | ##### | 46 | git_buf_encode_base64(buf, (const char *)msg, msg_len); | |
| 150 | - | |||
| 151 | ##### | 47,48 | if (git_buf_oom(buf)) | |
| 152 | ##### | 49 | goto done; | |
| 153 | - | |||
| 154 | ##### | 50 | error = 0; | |
| 155 | - | |||
| 156 | - | done: | ||
| 157 | ##### | 51 | git_buf_dispose(&input_buf); | |
| 158 | ##### | 52 | return error; | |
| 159 | - | } | ||
| 160 | - | |||
| 161 |  | ##### | 2 | static int ntlm_is_complete(git_http_auth_context *c) | 
| 162 | - | { | ||
| 163 | ##### | 2 | http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; | |
| 164 | - | |||
| 165 | ##### | 2,3 | assert(ctx); | |
| 166 | ##### | 4 | return (ctx->complete == true); | |
| 167 | - | } | ||
| 168 | - | |||
| 169 |  | ##### | 2 | static void ntlm_context_free(git_http_auth_context *c) | 
| 170 | - | { | ||
| 171 | ##### | 2 | http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c; | |
| 172 | - | |||
| 173 | ##### | 2 | ntlm_client_free(ctx->ntlm); | |
| 174 | ##### | 3 | git__free(ctx->challenge); | |
| 175 | ##### | 4 | git__free(ctx); | |
| 176 | ##### | 5 | } | |
| 177 | - | |||
| 178 |  | ##### | 2 | static int ntlm_init_context( | 
| 179 | - | http_auth_ntlm_context *ctx, | ||
| 180 | - | const git_net_url *url) | ||
| 181 | - | { | ||
| 182 | - | GIT_UNUSED(url); | ||
| 183 | - | |||
| 184 | ##### | 2,3 | if ((ctx->ntlm = ntlm_client_init(NTLM_CLIENT_DEFAULTS)) == NULL) { | |
| 185 | ##### | 4 | git_error_set_oom(); | |
| 186 | ##### | 5 | return -1; | |
| 187 | - | } | ||
| 188 | - | |||
| 189 | ##### | 6 | return 0; | |
| 190 | - | } | ||
| 191 | - | |||
| 192 |  | ##### | 2 | int git_http_auth_ntlm( | 
| 193 | - | git_http_auth_context **out, | ||
| 194 | - | const git_net_url *url) | ||
| 195 | - | { | ||
| 196 | - | http_auth_ntlm_context *ctx; | ||
| 197 | - | |||
| 198 | - | GIT_UNUSED(url); | ||
| 199 | - | |||
| 200 | ##### | 2 | *out = NULL; | |
| 201 | - | |||
| 202 | ##### | 2 | ctx = git__calloc(1, sizeof(http_auth_ntlm_context)); | |
| 203 | ##### | 3,4 | GIT_ERROR_CHECK_ALLOC(ctx); | |
| 204 | - | |||
| 205 | ##### | 5,6 | if (ntlm_init_context(ctx, url) < 0) { | |
| 206 | ##### | 7 | git__free(ctx); | |
| 207 | ##### | 8 | return -1; | |
| 208 | - | } | ||
| 209 | - | |||
| 210 | ##### | 9 | ctx->parent.type = GIT_HTTP_AUTH_NTLM; | |
| 211 | ##### | 9 | ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT; | |
| 212 | ##### | 9 | ctx->parent.connection_affinity = 1; | |
| 213 | ##### | 9 | ctx->parent.set_challenge = ntlm_set_challenge; | |
| 214 | ##### | 9 | ctx->parent.next_token = ntlm_next_token; | |
| 215 | ##### | 9 | ctx->parent.is_complete = ntlm_is_complete; | |
| 216 | ##### | 9 | ctx->parent.free = ntlm_context_free; | |
| 217 | - | |||
| 218 | ##### | 9 | *out = (git_http_auth_context *)ctx; | |
| 219 | - | |||
| 220 | ##### | 9 | return 0; | |
| 221 | - | } | ||
| 222 | - | |||
| 223 | - | #endif /* GIT_NTLM */ |