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
};
33 DATA_BLOB blob
, chal1
, chal2
, auth
;
35 uint8 nthash
[24], lmhash
[24], sess_key
[16];
37 struct berval cred
, *scred
;
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
);
74 /* the server gives us back two challenges */
75 if (!spnego_parse_challenge(blob
, &chal1
, &chal2
)) {
76 DEBUG(3,("Failed to parse challenges\n"));
77 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
81 data_blob_free(&blob
);
83 /* encrypt the password with the challenge */
84 memcpy(challenge
, chal1
.data
+ 24, 8);
85 SMBencrypt(ads
->auth
.password
, challenge
,lmhash
);
86 SMBNTencrypt(ads
->auth
.password
, challenge
,nthash
);
88 data_blob_free(&chal1
);
89 data_blob_free(&chal2
);
91 /* this generates the actual auth packet */
92 msrpc_gen(&blob
, "CdBBUUUBd",
103 /* wrap it in SPNEGO */
104 auth
= spnego_gen_auth(blob
);
106 data_blob_free(&blob
);
108 /* now send the auth packet and we should be done */
109 cred
.bv_val
= (char *)auth
.data
;
110 cred
.bv_len
= auth
.length
;
112 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
114 return ADS_ERROR(rc
);
121 perform a LDAP/SASL/SPNEGO/KRB5 bind
123 static ADS_STATUS
ads_sasl_spnego_krb5_bind(ADS_STRUCT
*ads
, const char *principal
)
126 struct berval cred
, *scred
;
127 unsigned char sk
[16];
130 blob
= spnego_gen_negTokenTarg(principal
, ads
->auth
.time_offset
, sk
);
133 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
136 /* now send the auth packet and we should be done */
137 cred
.bv_val
= (char *)blob
.data
;
138 cred
.bv_len
= blob
.length
;
140 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
142 data_blob_free(&blob
);
144 return ADS_ERROR(rc
);
148 this performs a SASL/SPNEGO bind
150 static ADS_STATUS
ads_sasl_spnego_bind(ADS_STRUCT
*ads
)
152 struct berval
*scred
=NULL
;
157 char *OIDs
[ASN1_MAX_OIDS
];
158 BOOL got_kerberos_mechanism
= False
;
160 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", NULL
, NULL
, NULL
, &scred
);
162 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
163 status
= ADS_ERROR(rc
);
167 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
170 file_save("sasl_spnego.dat", blob
.data
, blob
.length
);
173 /* the server sent us the first part of the SPNEGO exchange in the negprot
175 if (!spnego_parse_negTokenInit(blob
, OIDs
, &principal
)) {
176 data_blob_free(&blob
);
177 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
180 data_blob_free(&blob
);
182 /* make sure the server understands kerberos */
183 for (i
=0;OIDs
[i
];i
++) {
184 DEBUG(3,("got OID=%s\n", OIDs
[i
]));
185 if (strcmp(OIDs
[i
], OID_KERBEROS5_OLD
) == 0 ||
186 strcmp(OIDs
[i
], OID_KERBEROS5
) == 0) {
187 got_kerberos_mechanism
= True
;
191 DEBUG(3,("got principal=%s\n", principal
));
194 if (!(ads
->auth
.flags
& ADS_AUTH_DISABLE_KERBEROS
) &&
195 got_kerberos_mechanism
) {
196 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
197 if (ADS_ERR_OK(status
))
199 if (ads_kinit_password(ads
) == 0) {
200 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
202 /* only fallback to NTLMSSP if allowed */
203 if (ADS_ERR_OK(status
) ||
204 !(ads
->auth
.flags
& ADS_AUTH_ALLOW_NTLMSSP
)) {
210 /* lets do NTLMSSP ... this has the big advantage that we don't need
211 to sync clocks, and we don't rely on special versions of the krb5
212 library for HMAC_MD4 encryption */
213 return ads_sasl_spnego_ntlmssp_bind(ads
);
220 #define MAX_GSS_PASSES 3
222 /* this performs a SASL/gssapi bind
223 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
224 is very dependent on correctly configured DNS whereas
225 this routine is much less fragile
226 see RFC2078 and RFC2222 for details
228 static ADS_STATUS
ads_sasl_gssapi_bind(ADS_STRUCT
*ads
)
231 gss_name_t serv_name
;
232 gss_buffer_desc input_name
;
233 gss_ctx_id_t context_handle
;
234 gss_OID mech_type
= GSS_C_NULL_OID
;
235 gss_buffer_desc output_token
, input_token
;
236 uint32 ret_flags
, conf_state
;
238 struct berval
*scred
;
246 krb5_principal principal
;
248 krb5_enctype enc_types
[] = {
249 #ifdef ENCTYPE_ARCFOUR_HMAC
250 ENCTYPE_ARCFOUR_HMAC
,
254 gss_OID_desc nt_principal
=
255 {10, "\052\206\110\206\367\022\001\002\002\002"};
257 /* we need to fetch a service ticket as the ldap user in the
258 servers realm, regardless of our realm */
259 asprintf(&sname
, "ldap/%s@%s", ads
->config
.ldap_server_name
, ads
->config
.realm
);
260 krb5_init_context(&ctx
);
261 krb5_set_default_tgs_ktypes(ctx
, enc_types
);
262 krb5_parse_name(ctx
, sname
, &principal
);
264 krb5_free_context(ctx
);
266 input_name
.value
= &principal
;
267 input_name
.length
= sizeof(principal
);
269 gss_rc
= gss_import_name(&minor_status
,&input_name
,&nt_principal
, &serv_name
);
271 return ADS_ERROR_GSS(gss_rc
, minor_status
);
274 context_handle
= GSS_C_NO_CONTEXT
;
276 input_token
.value
= NULL
;
277 input_token
.length
= 0;
279 for (i
=0; i
< MAX_GSS_PASSES
; i
++) {
280 gss_rc
= gss_init_sec_context(&minor_status
,
285 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,
294 if (input_token
.value
) {
295 gss_release_buffer(&minor_status
, &input_token
);
298 if (gss_rc
&& gss_rc
!= GSS_S_CONTINUE_NEEDED
) {
299 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
303 cred
.bv_val
= output_token
.value
;
304 cred
.bv_len
= output_token
.length
;
306 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
308 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
309 status
= ADS_ERROR(rc
);
313 if (output_token
.value
) {
314 gss_release_buffer(&minor_status
, &output_token
);
318 input_token
.value
= scred
->bv_val
;
319 input_token
.length
= scred
->bv_len
;
321 input_token
.value
= NULL
;
322 input_token
.length
= 0;
325 if (gss_rc
== 0) break;
328 gss_release_name(&minor_status
, &serv_name
);
330 gss_rc
= gss_unwrap(&minor_status
,context_handle
,&input_token
,&output_token
,
331 (int *)&conf_state
,NULL
);
333 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
337 gss_release_buffer(&minor_status
, &input_token
);
339 p
= (uint8
*)output_token
.value
;
341 file_save("sasl_gssapi.dat", output_token
.value
, output_token
.length
);
343 max_msg_size
= (p
[1]<<16) | (p
[2]<<8) | p
[3];
346 gss_release_buffer(&minor_status
, &output_token
);
348 output_token
.value
= malloc(strlen(ads
->config
.bind_path
) + 8);
349 p
= output_token
.value
;
351 *p
++ = 1; /* no sign & seal selection */
352 /* choose the same size as the server gave us */
353 *p
++ = max_msg_size
>>16;
354 *p
++ = max_msg_size
>>8;
356 snprintf((char *)p
, strlen(ads
->config
.bind_path
)+4, "dn:%s", ads
->config
.bind_path
);
357 p
+= strlen((const char *)p
);
359 output_token
.length
= PTR_DIFF(p
, output_token
.value
);
361 gss_rc
= gss_wrap(&minor_status
, context_handle
,0,GSS_C_QOP_DEFAULT
,
362 &output_token
, (int *)&conf_state
,
365 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
369 free(output_token
.value
);
371 cred
.bv_val
= input_token
.value
;
372 cred
.bv_len
= input_token
.length
;
374 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
376 status
= ADS_ERROR(rc
);
378 gss_release_buffer(&minor_status
, &input_token
);
385 /* mapping between SASL mechanisms and functions */
388 ADS_STATUS (*fn
)(ADS_STRUCT
*);
389 } sasl_mechanisms
[] = {
390 {"GSS-SPNEGO", ads_sasl_spnego_bind
},
392 {"GSSAPI", ads_sasl_gssapi_bind
}, /* doesn't work with .NET RC1. No idea why */
397 ADS_STATUS
ads_sasl_bind(ADS_STRUCT
*ads
)
399 const char *attrs
[] = {"supportedSASLMechanisms", NULL
};
405 /* get a list of supported SASL mechanisms */
406 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
407 if (!ADS_ERR_OK(status
)) return status
;
409 values
= ldap_get_values(ads
->ld
, res
, "supportedSASLMechanisms");
411 /* try our supported mechanisms in order */
412 for (i
=0;sasl_mechanisms
[i
].name
;i
++) {
413 /* see if the server supports it */
414 for (j
=0;values
&& values
[j
];j
++) {
415 if (strcmp(values
[j
], sasl_mechanisms
[i
].name
) == 0) {
416 DEBUG(4,("Found SASL mechanism %s\n", values
[j
]));
417 status
= sasl_mechanisms
[i
].fn(ads
);
418 ldap_value_free(values
);
425 ldap_value_free(values
);
427 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED
);