ctdb-server: Remove duplicate logic
[samba4-gss.git] / libcli / http / gensec / generic.c
blob2f09b9d5eac6094ec65f81066f2addbd687c114e
1 /*
2 Unix SMB/CIFS implementation.
4 HTTP library - NTLM authentication mechanism gensec module
6 Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include <tevent.h>
24 #include "lib/util/tevent_ntstatus.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "auth/gensec/gensec_internal.h"
28 #include "lib/util/base64.h"
30 #undef strncasecmp
32 _PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *);
34 struct gensec_http_generic_state {
35 struct gensec_security *sub;
36 DATA_BLOB prefix;
39 static NTSTATUS gensec_http_generic_client_start(struct gensec_security *gensec,
40 const char *prefix_str,
41 const char *mech_oid)
43 NTSTATUS status;
44 struct gensec_http_generic_state *state;
46 state = talloc_zero(gensec, struct gensec_http_generic_state);
47 if (state == NULL) {
48 return NT_STATUS_NO_MEMORY;
50 gensec->private_data = state;
52 state->prefix = data_blob_string_const(prefix_str);
54 status = gensec_subcontext_start(state, gensec, &state->sub);
55 if (!NT_STATUS_IS_OK(status)) {
56 return status;
59 return gensec_start_mech_by_oid(state->sub, mech_oid);
62 static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
64 return gensec_http_generic_client_start(gensec, "NTLM",
65 GENSEC_OID_NTLMSSP);
68 static NTSTATUS gensec_http_negotiate_client_start(struct gensec_security *gensec)
70 return gensec_http_generic_client_start(gensec, "Negotiate",
71 GENSEC_OID_SPNEGO);
74 struct gensec_http_generic_update_state {
75 struct gensec_security *gensec;
76 DATA_BLOB sub_in;
77 NTSTATUS status;
78 DATA_BLOB out;
81 static void gensec_http_generic_update_done(struct tevent_req *subreq);
83 static struct tevent_req *gensec_http_generic_update_send(TALLOC_CTX *mem_ctx,
84 struct tevent_context *ev,
85 struct gensec_security *gensec_ctx,
86 const DATA_BLOB in)
88 struct gensec_http_generic_state *http_generic =
89 talloc_get_type_abort(gensec_ctx->private_data,
90 struct gensec_http_generic_state);
91 struct tevent_req *req = NULL;
92 struct gensec_http_generic_update_state *state = NULL;
93 struct tevent_req *subreq = NULL;
95 req = tevent_req_create(mem_ctx, &state,
96 struct gensec_http_generic_update_state);
97 if (req == NULL) {
98 return NULL;
100 state->gensec = gensec_ctx;
102 if (in.length) {
103 int cmp;
104 DATA_BLOB b64b;
105 size_t skip = 0;
107 if (in.length < http_generic->prefix.length) {
108 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
109 return tevent_req_post(req, ev);
112 cmp = strncasecmp((const char *)in.data,
113 (const char *)http_generic->prefix.data,
114 http_generic->prefix.length);
115 if (cmp != 0) {
116 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
117 return tevent_req_post(req, ev);
120 if (in.length == http_generic->prefix.length) {
122 * We expect more data, but the
123 * server just sent the prefix without
124 * a space prefixing base64 data.
126 * It means the server rejects
127 * the request with.
129 tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
130 return tevent_req_post(req, ev);
133 if (in.data[http_generic->prefix.length] != ' ') {
134 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
135 return tevent_req_post(req, ev);
137 skip = http_generic->prefix.length + 1;
139 b64b = data_blob_const(in.data + skip, in.length - skip);
140 if (b64b.length != 0) {
141 char *b64 = NULL;
144 * ensure it's terminated with \0' before
145 * passing to base64_decode_data_blob_talloc().
147 b64 = talloc_strndup(state, (const char *)b64b.data,
148 b64b.length);
149 if (tevent_req_nomem(b64, req)) {
150 return tevent_req_post(req, ev);
153 state->sub_in = base64_decode_data_blob_talloc(state,
154 b64);
155 TALLOC_FREE(b64);
156 if (tevent_req_nomem(state->sub_in.data, req)) {
157 return tevent_req_post(req, ev);
162 subreq = gensec_update_send(state, ev,
163 http_generic->sub,
164 state->sub_in);
165 if (tevent_req_nomem(subreq, req)) {
166 return tevent_req_post(req, ev);
168 tevent_req_set_callback(subreq, gensec_http_generic_update_done, req);
170 return req;
173 static void gensec_http_generic_update_done(struct tevent_req *subreq)
175 struct tevent_req *req =
176 tevent_req_callback_data(subreq,
177 struct tevent_req);
178 struct gensec_http_generic_update_state *state =
179 tevent_req_data(req,
180 struct gensec_http_generic_update_state);
181 struct gensec_http_generic_state *http_generic =
182 talloc_get_type_abort(state->gensec->private_data,
183 struct gensec_http_generic_state);
184 NTSTATUS status;
185 DATA_BLOB sub_out = data_blob_null;
186 char *b64 = NULL;
187 char *str = NULL;
188 int prefix_length;
190 status = gensec_update_recv(subreq, state, &sub_out);
191 TALLOC_FREE(subreq);
192 state->status = status;
193 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
194 status = NT_STATUS_OK;
196 if (tevent_req_nterror(req, status)) {
197 return;
200 if (sub_out.length == 0) {
201 tevent_req_done(req);
202 return;
205 b64 = base64_encode_data_blob(state, sub_out);
206 data_blob_free(&sub_out);
207 if (tevent_req_nomem(b64, req)) {
208 return;
211 prefix_length = http_generic->prefix.length;
212 str = talloc_asprintf(state, "%*.*s %s", prefix_length, prefix_length,
213 (const char *)http_generic->prefix.data, b64);
214 TALLOC_FREE(b64);
215 if (tevent_req_nomem(str, req)) {
216 return;
219 state->out = data_blob_string_const(str);
220 tevent_req_done(req);
223 static NTSTATUS gensec_http_generic_update_recv(struct tevent_req *req,
224 TALLOC_CTX *out_mem_ctx,
225 DATA_BLOB *out)
227 struct gensec_http_generic_update_state *state =
228 tevent_req_data(req,
229 struct gensec_http_generic_update_state);
230 NTSTATUS status;
232 *out = data_blob_null;
234 if (tevent_req_is_nterror(req, &status)) {
235 tevent_req_received(req);
236 return status;
239 *out = state->out;
240 talloc_steal(out_mem_ctx, state->out.data);
241 status = state->status;
242 tevent_req_received(req);
243 return status;
246 static const struct gensec_security_ops gensec_http_ntlm_security_ops = {
247 .name = "http_ntlm",
248 .auth_type = 0,
249 .client_start = gensec_http_ntlm_client_start,
250 .update_send = gensec_http_generic_update_send,
251 .update_recv = gensec_http_generic_update_recv,
252 .enabled = true,
253 .priority = GENSEC_EXTERNAL,
256 static const struct gensec_security_ops gensec_http_negotiate_security_ops = {
257 .name = "http_negotiate",
258 .auth_type = 0,
259 .client_start = gensec_http_negotiate_client_start,
260 .update_send = gensec_http_generic_update_send,
261 .update_recv = gensec_http_generic_update_recv,
262 .enabled = true,
263 .priority = GENSEC_EXTERNAL,
264 .glue = true,
267 _PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *ctx)
269 NTSTATUS status;
271 status = gensec_register(ctx, &gensec_http_ntlm_security_ops);
272 if (!NT_STATUS_IS_OK(status)) {
273 DEBUG(0, ("Failed to register '%s' gensec backend!\n",
274 gensec_http_ntlm_security_ops.name));
275 return status;
278 status = gensec_register(ctx, &gensec_http_negotiate_security_ops);
279 if (!NT_STATUS_IS_OK(status)) {
280 DEBUG(0, ("Failed to register '%s' gensec backend!\n",
281 gensec_http_negotiate_security_ops.name));
282 return status;
285 return status;