r20132: get rid of defined but not used warning - static function only used
[Samba.git] / source3 / libads / sasl.c
blob7d1fd0d1a839749ef2160d1762c33de5a8a8a4a9
1 /*
2 Unix SMB/CIFS implementation.
3 ads sasl code
4 Copyright (C) Andrew Tridgell 2001
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "includes.h"
23 #ifdef HAVE_LDAP
25 /*
26 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27 we fit on one socket??)
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
31 DATA_BLOB msg1 = data_blob(NULL, 0);
32 DATA_BLOB blob = data_blob(NULL, 0);
33 DATA_BLOB blob_in = data_blob(NULL, 0);
34 DATA_BLOB blob_out = data_blob(NULL, 0);
35 struct berval cred, *scred = NULL;
36 int rc;
37 NTSTATUS nt_status;
38 int turn = 1;
40 struct ntlmssp_state *ntlmssp_state;
42 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
43 return ADS_ERROR_NT(nt_status);
45 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
47 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
48 return ADS_ERROR_NT(nt_status);
50 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
51 return ADS_ERROR_NT(nt_status);
53 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
54 return ADS_ERROR_NT(nt_status);
57 blob_in = data_blob(NULL, 0);
59 do {
60 nt_status = ntlmssp_update(ntlmssp_state,
61 blob_in, &blob_out);
62 data_blob_free(&blob_in);
63 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
64 || NT_STATUS_IS_OK(nt_status))
65 && blob_out.length) {
66 if (turn == 1) {
67 /* and wrap it in a SPNEGO wrapper */
68 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
69 } else {
70 /* wrap it in SPNEGO */
71 msg1 = spnego_gen_auth(blob_out);
74 data_blob_free(&blob_out);
76 cred.bv_val = (char *)msg1.data;
77 cred.bv_len = msg1.length;
78 scred = NULL;
79 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
80 data_blob_free(&msg1);
81 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
82 if (scred) {
83 ber_bvfree(scred);
86 ntlmssp_end(&ntlmssp_state);
87 return ADS_ERROR(rc);
89 if (scred) {
90 blob = data_blob(scred->bv_val, scred->bv_len);
91 ber_bvfree(scred);
92 } else {
93 blob = data_blob(NULL, 0);
96 } else {
98 ntlmssp_end(&ntlmssp_state);
99 data_blob_free(&blob_out);
100 return ADS_ERROR_NT(nt_status);
103 if ((turn == 1) &&
104 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
105 DATA_BLOB tmp_blob = data_blob(NULL, 0);
106 /* the server might give us back two challenges */
107 if (!spnego_parse_challenge(blob, &blob_in,
108 &tmp_blob)) {
110 ntlmssp_end(&ntlmssp_state);
111 data_blob_free(&blob);
112 DEBUG(3,("Failed to parse challenges\n"));
113 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
115 data_blob_free(&tmp_blob);
116 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
117 if (!spnego_parse_auth_response(blob, nt_status,
118 &blob_in)) {
120 ntlmssp_end(&ntlmssp_state);
121 data_blob_free(&blob);
122 DEBUG(3,("Failed to parse auth response\n"));
123 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
126 data_blob_free(&blob);
127 data_blob_free(&blob_out);
128 turn++;
129 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
131 /* we have a reference conter on ntlmssp_state, if we are signing
132 then the state will be kept by the signing engine */
134 ntlmssp_end(&ntlmssp_state);
136 return ADS_ERROR(rc);
139 #ifdef HAVE_KRB5
141 perform a LDAP/SASL/SPNEGO/KRB5 bind
143 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
145 DATA_BLOB blob = data_blob(NULL, 0);
146 struct berval cred, *scred = NULL;
147 DATA_BLOB session_key = data_blob(NULL, 0);
148 int rc;
150 rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0);
152 if (rc) {
153 return ADS_ERROR_KRB5(rc);
156 /* now send the auth packet and we should be done */
157 cred.bv_val = (char *)blob.data;
158 cred.bv_len = blob.length;
160 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
162 data_blob_free(&blob);
163 data_blob_free(&session_key);
164 if(scred)
165 ber_bvfree(scred);
167 return ADS_ERROR(rc);
169 #endif
172 this performs a SASL/SPNEGO bind
174 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
176 struct berval *scred=NULL;
177 int rc, i;
178 ADS_STATUS status;
179 DATA_BLOB blob;
180 char *principal = NULL;
181 char *OIDs[ASN1_MAX_OIDS];
182 #ifdef HAVE_KRB5
183 BOOL got_kerberos_mechanism = False;
184 #endif
186 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
188 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
189 status = ADS_ERROR(rc);
190 goto failed;
193 blob = data_blob(scred->bv_val, scred->bv_len);
195 ber_bvfree(scred);
197 #if 0
198 file_save("sasl_spnego.dat", blob.data, blob.length);
199 #endif
201 /* the server sent us the first part of the SPNEGO exchange in the negprot
202 reply */
203 if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
204 data_blob_free(&blob);
205 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
206 goto failed;
208 data_blob_free(&blob);
210 /* make sure the server understands kerberos */
211 for (i=0;OIDs[i];i++) {
212 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
213 #ifdef HAVE_KRB5
214 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
215 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
216 got_kerberos_mechanism = True;
218 #endif
219 free(OIDs[i]);
221 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
223 #ifdef HAVE_KRB5
224 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
225 got_kerberos_mechanism) {
226 status = ads_sasl_spnego_krb5_bind(ads, principal);
227 if (ADS_ERR_OK(status)) {
228 SAFE_FREE(principal);
229 return status;
232 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
234 if (ADS_ERR_OK(status)) {
235 status = ads_sasl_spnego_krb5_bind(ads, principal);
238 /* only fallback to NTLMSSP if allowed */
239 if (ADS_ERR_OK(status) ||
240 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
241 SAFE_FREE(principal);
242 return status;
245 #endif
247 SAFE_FREE(principal);
249 /* lets do NTLMSSP ... this has the big advantage that we don't need
250 to sync clocks, and we don't rely on special versions of the krb5
251 library for HMAC_MD4 encryption */
252 return ads_sasl_spnego_ntlmssp_bind(ads);
254 failed:
255 return status;
258 #ifdef HAVE_GSSAPI
259 #define MAX_GSS_PASSES 3
261 /* this performs a SASL/gssapi bind
262 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
263 is very dependent on correctly configured DNS whereas
264 this routine is much less fragile
265 see RFC2078 and RFC2222 for details
267 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
269 uint32 minor_status;
270 gss_name_t serv_name;
271 gss_buffer_desc input_name;
272 gss_ctx_id_t context_handle;
273 gss_OID mech_type = GSS_C_NULL_OID;
274 gss_buffer_desc output_token, input_token;
275 uint32 ret_flags, conf_state;
276 struct berval cred;
277 struct berval *scred = NULL;
278 int i=0;
279 int gss_rc, rc;
280 uint8 *p;
281 uint32 max_msg_size = 0;
282 char *sname;
283 ADS_STATUS status;
284 krb5_principal principal;
285 krb5_context ctx = NULL;
286 krb5_enctype enc_types[] = {
287 #ifdef ENCTYPE_ARCFOUR_HMAC
288 ENCTYPE_ARCFOUR_HMAC,
289 #endif
290 ENCTYPE_DES_CBC_MD5,
291 ENCTYPE_NULL};
292 gss_OID_desc nt_principal =
293 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
295 /* we need to fetch a service ticket as the ldap user in the
296 servers realm, regardless of our realm */
297 asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
299 initialize_krb5_error_table();
300 status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
301 if (!ADS_ERR_OK(status)) {
302 return status;
304 status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
305 if (!ADS_ERR_OK(status)) {
306 return status;
308 status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
309 if (!ADS_ERR_OK(status)) {
310 return status;
313 free(sname);
314 krb5_free_context(ctx);
316 input_name.value = &principal;
317 input_name.length = sizeof(principal);
319 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
320 if (gss_rc) {
321 return ADS_ERROR_GSS(gss_rc, minor_status);
324 context_handle = GSS_C_NO_CONTEXT;
326 input_token.value = NULL;
327 input_token.length = 0;
329 for (i=0; i < MAX_GSS_PASSES; i++) {
330 gss_rc = gss_init_sec_context(&minor_status,
331 GSS_C_NO_CREDENTIAL,
332 &context_handle,
333 serv_name,
334 mech_type,
335 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
337 NULL,
338 &input_token,
339 NULL,
340 &output_token,
341 &ret_flags,
342 NULL);
344 if (input_token.value) {
345 gss_release_buffer(&minor_status, &input_token);
348 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
349 status = ADS_ERROR_GSS(gss_rc, minor_status);
350 goto failed;
353 cred.bv_val = (char *)output_token.value;
354 cred.bv_len = output_token.length;
356 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
357 &scred);
358 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
359 status = ADS_ERROR(rc);
360 goto failed;
363 if (output_token.value) {
364 gss_release_buffer(&minor_status, &output_token);
367 if (scred) {
368 input_token.value = scred->bv_val;
369 input_token.length = scred->bv_len;
370 } else {
371 input_token.value = NULL;
372 input_token.length = 0;
375 if (gss_rc == 0) break;
378 gss_release_name(&minor_status, &serv_name);
380 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
381 (int *)&conf_state,NULL);
382 if (gss_rc) {
383 status = ADS_ERROR_GSS(gss_rc, minor_status);
384 goto failed;
387 gss_release_buffer(&minor_status, &input_token);
389 p = (uint8 *)output_token.value;
391 #if 0
392 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
393 #endif
395 if (p) {
396 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
399 gss_release_buffer(&minor_status, &output_token);
401 output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
402 p = (uint8 *)output_token.value;
404 *p++ = 1; /* no sign & seal selection */
405 /* choose the same size as the server gave us */
406 *p++ = max_msg_size>>16;
407 *p++ = max_msg_size>>8;
408 *p++ = max_msg_size;
409 snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
410 p += strlen((const char *)p);
412 output_token.length = PTR_DIFF(p, output_token.value);
414 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
415 &output_token, (int *)&conf_state,
416 &input_token);
417 if (gss_rc) {
418 status = ADS_ERROR_GSS(gss_rc, minor_status);
419 goto failed;
422 free(output_token.value);
424 cred.bv_val = (char *)input_token.value;
425 cred.bv_len = input_token.length;
427 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL,
428 &scred);
429 status = ADS_ERROR(rc);
431 gss_release_buffer(&minor_status, &input_token);
433 failed:
434 if(scred)
435 ber_bvfree(scred);
436 return status;
438 #endif /* HAVE_GGSAPI */
440 /* mapping between SASL mechanisms and functions */
441 static struct {
442 const char *name;
443 ADS_STATUS (*fn)(ADS_STRUCT *);
444 } sasl_mechanisms[] = {
445 {"GSS-SPNEGO", ads_sasl_spnego_bind},
446 #ifdef HAVE_GSSAPI
447 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
448 #endif
449 {NULL, NULL}
452 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
454 const char *attrs[] = {"supportedSASLMechanisms", NULL};
455 char **values;
456 ADS_STATUS status;
457 int i, j;
458 LDAPMessage *res;
460 /* get a list of supported SASL mechanisms */
461 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
462 if (!ADS_ERR_OK(status)) return status;
464 values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
466 /* try our supported mechanisms in order */
467 for (i=0;sasl_mechanisms[i].name;i++) {
468 /* see if the server supports it */
469 for (j=0;values && values[j];j++) {
470 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
471 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
472 status = sasl_mechanisms[i].fn(ads);
473 ldap_value_free(values);
474 ldap_msgfree(res);
475 return status;
480 ldap_value_free(values);
481 ldap_msgfree(res);
482 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
485 #endif /* HAVE_LDAP */