2 Unix SMB/CIFS implementation.
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.
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 const char *mechs
[] = {OID_NTLMSSP
, NULL
};
32 DATA_BLOB msg1
= data_blob(NULL
, 0);
33 DATA_BLOB blob
, chal1
, chal2
, auth
;
35 uint8 nthash
[24], lmhash
[24], sess_key
[16];
37 struct berval cred
, *scred
= NULL
;
41 if (!ads
->auth
.password
) {
42 /* No password, don't segfault below... */
43 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE
);
46 neg_flags
= NTLMSSP_NEGOTIATE_UNICODE
|
47 NTLMSSP_NEGOTIATE_128
|
48 NTLMSSP_NEGOTIATE_NTLM
;
50 memset(sess_key
, 0, 16);
52 /* generate the ntlmssp negotiate packet */
53 msrpc_gen(&blob
, "CddB",
59 /* and wrap it in a SPNEGO wrapper */
60 msg1
= gen_negTokenTarg(mechs
, blob
);
61 data_blob_free(&blob
);
63 cred
.bv_val
= (char *)msg1
.data
;
64 cred
.bv_len
= msg1
.length
;
66 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
67 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
68 status
= ADS_ERROR(rc
);
72 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
75 /* the server gives us back two challenges */
76 if (!spnego_parse_challenge(blob
, &chal1
, &chal2
)) {
77 DEBUG(3,("Failed to parse challenges\n"));
78 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
82 data_blob_free(&blob
);
84 /* encrypt the password with the challenge */
85 memcpy(challenge
, chal1
.data
+ 24, 8);
86 SMBencrypt(ads
->auth
.password
, challenge
,lmhash
);
87 SMBNTencrypt(ads
->auth
.password
, challenge
,nthash
);
89 data_blob_free(&chal1
);
90 data_blob_free(&chal2
);
92 /* this generates the actual auth packet */
93 msrpc_gen(&blob
, "CdBBUUUBd",
104 /* wrap it in SPNEGO */
105 auth
= spnego_gen_auth(blob
);
107 data_blob_free(&blob
);
109 /* Remember to free the msg1 blob. The contents of this
110 have been copied into cred and need freeing before reassignment. */
111 data_blob_free(&msg1
);
113 /* now send the auth packet and we should be done */
114 cred
.bv_val
= (char *)auth
.data
;
115 cred
.bv_len
= auth
.length
;
117 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
120 data_blob_free(&auth
);
122 return ADS_ERROR(rc
);
126 /* Remember to free the msg1 blob. The contents of this
127 have been copied into cred and need freeing. */
128 data_blob_free(&msg1
);
136 perform a LDAP/SASL/SPNEGO/KRB5 bind
138 static ADS_STATUS
ads_sasl_spnego_krb5_bind(ADS_STRUCT
*ads
, const char *principal
)
140 DATA_BLOB blob
= data_blob(NULL
, 0);
141 struct berval cred
, *scred
= NULL
;
142 DATA_BLOB session_key
= data_blob(NULL
, 0);
145 rc
= spnego_gen_negTokenTarg(principal
, ads
->auth
.time_offset
, &blob
, &session_key
);
148 return ADS_ERROR_KRB5(rc
);
151 /* now send the auth packet and we should be done */
152 cred
.bv_val
= (char *)blob
.data
;
153 cred
.bv_len
= blob
.length
;
155 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
157 data_blob_free(&blob
);
158 data_blob_free(&session_key
);
162 return ADS_ERROR(rc
);
166 this performs a SASL/SPNEGO bind
168 static ADS_STATUS
ads_sasl_spnego_bind(ADS_STRUCT
*ads
)
170 struct berval
*scred
=NULL
;
174 char *principal
= NULL
;
175 char *OIDs
[ASN1_MAX_OIDS
];
176 BOOL got_kerberos_mechanism
= False
;
178 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", NULL
, NULL
, NULL
, &scred
);
180 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
181 status
= ADS_ERROR(rc
);
185 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
190 file_save("sasl_spnego.dat", blob
.data
, blob
.length
);
193 /* the server sent us the first part of the SPNEGO exchange in the negprot
195 if (!spnego_parse_negTokenInit(blob
, OIDs
, &principal
)) {
196 data_blob_free(&blob
);
197 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
200 data_blob_free(&blob
);
202 /* make sure the server understands kerberos */
203 for (i
=0;OIDs
[i
];i
++) {
204 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs
[i
]));
205 if (strcmp(OIDs
[i
], OID_KERBEROS5_OLD
) == 0 ||
206 strcmp(OIDs
[i
], OID_KERBEROS5
) == 0) {
207 got_kerberos_mechanism
= True
;
211 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal
));
214 if (!(ads
->auth
.flags
& ADS_AUTH_DISABLE_KERBEROS
) &&
215 got_kerberos_mechanism
) {
216 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
217 if (ADS_ERR_OK(status
)) {
218 SAFE_FREE(principal
);
222 status
= ADS_ERROR_KRB5(ads_kinit_password(ads
));
224 if (ADS_ERR_OK(status
)) {
225 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
228 /* only fallback to NTLMSSP if allowed */
229 if (ADS_ERR_OK(status
) ||
230 !(ads
->auth
.flags
& ADS_AUTH_ALLOW_NTLMSSP
)) {
231 SAFE_FREE(principal
);
237 SAFE_FREE(principal
);
239 /* lets do NTLMSSP ... this has the big advantage that we don't need
240 to sync clocks, and we don't rely on special versions of the krb5
241 library for HMAC_MD4 encryption */
242 return ads_sasl_spnego_ntlmssp_bind(ads
);
249 #define MAX_GSS_PASSES 3
251 /* this performs a SASL/gssapi bind
252 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
253 is very dependent on correctly configured DNS whereas
254 this routine is much less fragile
255 see RFC2078 and RFC2222 for details
257 static ADS_STATUS
ads_sasl_gssapi_bind(ADS_STRUCT
*ads
)
260 gss_name_t serv_name
;
261 gss_buffer_desc input_name
;
262 gss_ctx_id_t context_handle
;
263 gss_OID mech_type
= GSS_C_NULL_OID
;
264 gss_buffer_desc output_token
, input_token
;
265 uint32 ret_flags
, conf_state
;
267 struct berval
*scred
= NULL
;
275 krb5_principal principal
;
276 krb5_context ctx
= NULL
;
277 krb5_enctype enc_types
[] = {
278 #ifdef ENCTYPE_ARCFOUR_HMAC
279 ENCTYPE_ARCFOUR_HMAC
,
283 gss_OID_desc nt_principal
=
284 {10, "\052\206\110\206\367\022\001\002\002\002"};
286 /* we need to fetch a service ticket as the ldap user in the
287 servers realm, regardless of our realm */
288 asprintf(&sname
, "ldap/%s@%s", ads
->config
.ldap_server_name
, ads
->config
.realm
);
289 krb5_init_context(&ctx
);
290 krb5_set_default_tgs_ktypes(ctx
, enc_types
);
291 krb5_parse_name(ctx
, sname
, &principal
);
293 krb5_free_context(ctx
);
295 input_name
.value
= &principal
;
296 input_name
.length
= sizeof(principal
);
298 gss_rc
= gss_import_name(&minor_status
,&input_name
,&nt_principal
, &serv_name
);
300 return ADS_ERROR_GSS(gss_rc
, minor_status
);
303 context_handle
= GSS_C_NO_CONTEXT
;
305 input_token
.value
= NULL
;
306 input_token
.length
= 0;
308 for (i
=0; i
< MAX_GSS_PASSES
; i
++) {
309 gss_rc
= gss_init_sec_context(&minor_status
,
314 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,
323 if (input_token
.value
) {
324 gss_release_buffer(&minor_status
, &input_token
);
327 if (gss_rc
&& gss_rc
!= GSS_S_CONTINUE_NEEDED
) {
328 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
332 cred
.bv_val
= output_token
.value
;
333 cred
.bv_len
= output_token
.length
;
335 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
337 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
338 status
= ADS_ERROR(rc
);
342 if (output_token
.value
) {
343 gss_release_buffer(&minor_status
, &output_token
);
347 input_token
.value
= scred
->bv_val
;
348 input_token
.length
= scred
->bv_len
;
350 input_token
.value
= NULL
;
351 input_token
.length
= 0;
354 if (gss_rc
== 0) break;
357 gss_release_name(&minor_status
, &serv_name
);
359 gss_rc
= gss_unwrap(&minor_status
,context_handle
,&input_token
,&output_token
,
360 (int *)&conf_state
,NULL
);
362 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
366 gss_release_buffer(&minor_status
, &input_token
);
368 p
= (uint8
*)output_token
.value
;
370 file_save("sasl_gssapi.dat", output_token
.value
, output_token
.length
);
372 max_msg_size
= (p
[1]<<16) | (p
[2]<<8) | p
[3];
375 gss_release_buffer(&minor_status
, &output_token
);
377 output_token
.value
= malloc(strlen(ads
->config
.bind_path
) + 8);
378 p
= output_token
.value
;
380 *p
++ = 1; /* no sign & seal selection */
381 /* choose the same size as the server gave us */
382 *p
++ = max_msg_size
>>16;
383 *p
++ = max_msg_size
>>8;
385 snprintf((char *)p
, strlen(ads
->config
.bind_path
)+4, "dn:%s", ads
->config
.bind_path
);
386 p
+= strlen((const char *)p
);
388 output_token
.length
= PTR_DIFF(p
, output_token
.value
);
390 gss_rc
= gss_wrap(&minor_status
, context_handle
,0,GSS_C_QOP_DEFAULT
,
391 &output_token
, (int *)&conf_state
,
394 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
398 free(output_token
.value
);
400 cred
.bv_val
= input_token
.value
;
401 cred
.bv_len
= input_token
.length
;
403 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
405 status
= ADS_ERROR(rc
);
407 gss_release_buffer(&minor_status
, &input_token
);
416 /* mapping between SASL mechanisms and functions */
419 ADS_STATUS (*fn
)(ADS_STRUCT
*);
420 } sasl_mechanisms
[] = {
421 {"GSS-SPNEGO", ads_sasl_spnego_bind
},
423 {"GSSAPI", ads_sasl_gssapi_bind
}, /* doesn't work with .NET RC1. No idea why */
428 ADS_STATUS
ads_sasl_bind(ADS_STRUCT
*ads
)
430 const char *attrs
[] = {"supportedSASLMechanisms", NULL
};
436 /* get a list of supported SASL mechanisms */
437 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
438 if (!ADS_ERR_OK(status
)) return status
;
440 values
= ldap_get_values(ads
->ld
, res
, "supportedSASLMechanisms");
442 /* try our supported mechanisms in order */
443 for (i
=0;sasl_mechanisms
[i
].name
;i
++) {
444 /* see if the server supports it */
445 for (j
=0;values
&& values
[j
];j
++) {
446 if (strcmp(values
[j
], sasl_mechanisms
[i
].name
) == 0) {
447 DEBUG(4,("Found SASL mechanism %s\n", values
[j
]));
448 status
= sasl_mechanisms
[i
].fn(ads
);
449 ldap_value_free(values
);
456 ldap_value_free(values
);
458 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED
);