This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[samba.git] / source3 / libads / sasl.c
1 /* 
2    Unix SMB/CIFS implementation.
3    ads sasl code
4    Copyright (C) Andrew Tridgell 2001
5    
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.
10    
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.
15    
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.
19 */
20
21 #include "includes.h"
22
23 #ifdef HAVE_ADS
24
25 /* 
26    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27    we fit on one socket??)
28 */
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
30 {
31         const char *mechs[] = {OID_NTLMSSP, NULL};
32         DATA_BLOB msg1;
33         DATA_BLOB blob, chal1, chal2, auth;
34         uint8 challenge[8];
35         uint8 nthash[24], lmhash[24], sess_key[16];
36         uint32 neg_flags;
37         struct berval cred, *scred;
38         ADS_STATUS status;
39         extern pstring global_myname;
40         int rc;
41
42         if (!ads->auth.password) {
43                 /* No password, don't segfault below... */
44                 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE);
45         }
46
47         neg_flags = NTLMSSP_NEGOTIATE_UNICODE | 
48                 NTLMSSP_NEGOTIATE_128 | 
49                 NTLMSSP_NEGOTIATE_NTLM;
50
51         memset(sess_key, 0, 16);
52
53         /* generate the ntlmssp negotiate packet */
54         msrpc_gen(&blob, "CddB",
55                   "NTLMSSP",
56                   NTLMSSP_NEGOTIATE,
57                   neg_flags,
58                   sess_key, 16);
59
60         /* and wrap it in a SPNEGO wrapper */
61         msg1 = gen_negTokenTarg(mechs, blob);
62         data_blob_free(&blob);
63
64         cred.bv_val = msg1.data;
65         cred.bv_len = msg1.length;
66
67         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
68         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
69                 status = ADS_ERROR(rc);
70                 goto failed;
71         }
72
73         blob = data_blob(scred->bv_val, scred->bv_len);
74
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);
79                 goto failed;
80         }
81
82         data_blob_free(&blob);
83
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);
88
89         data_blob_free(&chal1);
90         data_blob_free(&chal2);
91
92         /* this generates the actual auth packet */
93         msrpc_gen(&blob, "CdBBUUUBd", 
94                   "NTLMSSP", 
95                   NTLMSSP_AUTH, 
96                   lmhash, 24,
97                   nthash, 24,
98                   lp_workgroup(), 
99                   ads->auth.user_name, 
100                   global_myname,
101                   sess_key, 16,
102                   neg_flags);
103
104         /* wrap it in SPNEGO */
105         auth = spnego_gen_auth(blob);
106
107         data_blob_free(&blob);
108
109         /* now send the auth packet and we should be done */
110         cred.bv_val = auth.data;
111         cred.bv_len = auth.length;
112
113         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
114
115         return ADS_ERROR(rc);
116
117 failed:
118         return status;
119 }
120
121 /* 
122    perform a LDAP/SASL/SPNEGO/KRB5 bind
123 */
124 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
125 {
126         DATA_BLOB blob;
127         struct berval cred, *scred;
128         int rc;
129
130         blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset);
131
132         if (!blob.data) {
133                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
134         }
135
136         /* now send the auth packet and we should be done */
137         cred.bv_val = blob.data;
138         cred.bv_len = blob.length;
139
140         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
141
142         data_blob_free(&blob);
143
144         return ADS_ERROR(rc);
145 }
146
147 /* 
148    this performs a SASL/SPNEGO bind
149 */
150 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
151 {
152         struct berval *scred=NULL;
153         int rc, i;
154         ADS_STATUS status;
155         DATA_BLOB blob;
156         char *principal;
157         char *OIDs[ASN1_MAX_OIDS];
158         BOOL got_kerberos_mechanism = False;
159
160         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
161
162         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
163                 status = ADS_ERROR(rc);
164                 goto failed;
165         }
166
167         blob = data_blob(scred->bv_val, scred->bv_len);
168
169 #if 0
170         file_save("sasl_spnego.dat", blob.data, blob.length);
171 #endif
172
173         /* the server sent us the first part of the SPNEGO exchange in the negprot 
174            reply */
175         if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
176                 data_blob_free(&blob);
177                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
178                 goto failed;
179         }
180         data_blob_free(&blob);
181
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;
188                 }
189                 free(OIDs[i]);
190         }
191         DEBUG(3,("got principal=%s\n", principal));
192
193         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
194             got_kerberos_mechanism && ads_kinit_password(ads) == 0) {
195                 return ads_sasl_spnego_krb5_bind(ads, principal);
196         }
197
198         /* lets do NTLMSSP ... this has the big advantage that we don't need
199            to sync clocks, and we don't rely on special versions of the krb5 
200            library for HMAC_MD4 encryption */
201         return ads_sasl_spnego_ntlmssp_bind(ads);
202
203 failed:
204         return status;
205 }
206
207 #ifdef HAVE_GSSAPI
208 #define MAX_GSS_PASSES 3
209
210 /* this performs a SASL/gssapi bind
211    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
212    is very dependent on correctly configured DNS whereas
213    this routine is much less fragile
214    see RFC2078 and RFC2222 for details
215 */
216 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
217 {
218         int minor_status;
219         gss_name_t serv_name;
220         gss_buffer_desc input_name;
221         gss_ctx_id_t context_handle;
222         gss_OID mech_type = GSS_C_NULL_OID;
223         gss_buffer_desc output_token, input_token;
224         OM_uint32 ret_flags, conf_state;
225         struct berval cred;
226         struct berval *scred;
227         int i=0;
228         int gss_rc, rc;
229         uint8 *p;
230         uint32 max_msg_size;
231         char *sname;
232         unsigned sec_layer;
233         ADS_STATUS status;
234         krb5_principal principal;
235         krb5_context ctx;
236         krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
237         gss_OID_desc nt_principal = 
238         {10, "\052\206\110\206\367\022\001\002\002\002"};
239
240         /* we need to fetch a service ticket as the ldap user in the
241            servers realm, regardless of our realm */
242         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
243         krb5_init_context(&ctx);
244         krb5_set_default_tgs_ktypes(ctx, enc_types);
245         krb5_parse_name(ctx, sname, &principal);
246         free(sname);
247         krb5_free_context(ctx); 
248
249         input_name.value = &principal;
250         input_name.length = sizeof(principal);
251
252         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
253         if (gss_rc) {
254                 return ADS_ERROR_GSS(gss_rc, minor_status);
255         }
256
257         context_handle = GSS_C_NO_CONTEXT;
258
259         input_token.value = NULL;
260         input_token.length = 0;
261
262         for (i=0; i < MAX_GSS_PASSES; i++) {
263                 gss_rc = gss_init_sec_context(&minor_status,
264                                           GSS_C_NO_CREDENTIAL,
265                                           &context_handle,
266                                           serv_name,
267                                           mech_type,
268                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
269                                           0,
270                                           NULL,
271                                           &input_token,
272                                           NULL,
273                                           &output_token,
274                                           &ret_flags,
275                                           NULL);
276
277                 if (input_token.value) {
278                         gss_release_buffer(&minor_status, &input_token);
279                 }
280
281                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
282                         status = ADS_ERROR_GSS(gss_rc, minor_status);
283                         goto failed;
284                 }
285
286                 cred.bv_val = output_token.value;
287                 cred.bv_len = output_token.length;
288
289                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
290                                       &scred);
291                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
292                         status = ADS_ERROR(rc);
293                         goto failed;
294                 }
295
296                 if (output_token.value) {
297                         gss_release_buffer(&minor_status, &output_token);
298                 }
299
300                 if (scred) {
301                         input_token.value = scred->bv_val;
302                         input_token.length = scred->bv_len;
303                 } else {
304                         input_token.value = NULL;
305                         input_token.length = 0;
306                 }
307
308                 if (gss_rc == 0) break;
309         }
310
311         gss_release_name(&minor_status, &serv_name);
312
313         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
314                             &conf_state,NULL);
315         if (gss_rc) {
316                 status = ADS_ERROR_GSS(gss_rc, minor_status);
317                 goto failed;
318         }
319
320         gss_release_buffer(&minor_status, &input_token);
321
322         p = (uint8 *)output_token.value;
323
324         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
325
326         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
327         sec_layer = *p;
328
329         gss_release_buffer(&minor_status, &output_token);
330
331         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
332         p = output_token.value;
333
334         *p++ = 1; /* no sign & seal selection */
335         /* choose the same size as the server gave us */
336         *p++ = max_msg_size>>16;
337         *p++ = max_msg_size>>8;
338         *p++ = max_msg_size;
339         snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
340         p += strlen(p);
341
342         output_token.length = PTR_DIFF(p, output_token.value);
343
344         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
345                           &output_token, &conf_state,
346                           &input_token);
347         if (gss_rc) {
348                 status = ADS_ERROR_GSS(gss_rc, minor_status);
349                 goto failed;
350         }
351
352         free(output_token.value);
353
354         cred.bv_val = input_token.value;
355         cred.bv_len = input_token.length;
356
357         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
358                               &scred);
359         status = ADS_ERROR(rc);
360
361         gss_release_buffer(&minor_status, &input_token);
362
363 failed:
364         return status;
365 }
366 #endif
367
368 /* mapping between SASL mechanisms and functions */
369 static struct {
370         const char *name;
371         ADS_STATUS (*fn)(ADS_STRUCT *);
372 } sasl_mechanisms[] = {
373         {"GSS-SPNEGO", ads_sasl_spnego_bind},
374 #ifdef HAVE_GSSAPI
375         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
376 #endif
377         {NULL, NULL}
378 };
379
380 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
381 {
382         const char *attrs[] = {"supportedSASLMechanisms", NULL};
383         char **values;
384         ADS_STATUS status;
385         int i, j;
386         void *res;
387
388         /* get a list of supported SASL mechanisms */
389         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
390         if (!ADS_ERR_OK(status)) return status;
391
392         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
393
394         /* try our supported mechanisms in order */
395         for (i=0;sasl_mechanisms[i].name;i++) {
396                 /* see if the server supports it */
397                 for (j=0;values && values[j];j++) {
398                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
399                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
400                                 status = sasl_mechanisms[i].fn(ads);
401                                 ldap_value_free(values);
402                                 ldap_msgfree(res);
403                                 return status;
404                         }
405                 }
406         }
407
408         ldap_value_free(values);
409         ldap_msgfree(res);
410         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
411 }
412
413 #endif
414