This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.
[kai/samba-autobuild/.git] / source / 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_LDAP
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 #ifdef HAVE_KRB5
194         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
195             got_kerberos_mechanism && ads_kinit_password(ads) == 0) {
196                 return ads_sasl_spnego_krb5_bind(ads, principal);
197         }
198 #endif
199
200         /* lets do NTLMSSP ... this has the big advantage that we don't need
201            to sync clocks, and we don't rely on special versions of the krb5 
202            library for HMAC_MD4 encryption */
203         return ads_sasl_spnego_ntlmssp_bind(ads);
204
205 failed:
206         return status;
207 }
208
209 #ifdef HAVE_GSSAPI
210 #define MAX_GSS_PASSES 3
211
212 /* this performs a SASL/gssapi bind
213    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
214    is very dependent on correctly configured DNS whereas
215    this routine is much less fragile
216    see RFC2078 and RFC2222 for details
217 */
218 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
219 {
220         int minor_status;
221         gss_name_t serv_name;
222         gss_buffer_desc input_name;
223         gss_ctx_id_t context_handle;
224         gss_OID mech_type = GSS_C_NULL_OID;
225         gss_buffer_desc output_token, input_token;
226         OM_uint32 ret_flags, conf_state;
227         struct berval cred;
228         struct berval *scred;
229         int i=0;
230         int gss_rc, rc;
231         uint8 *p;
232         uint32 max_msg_size;
233         char *sname;
234         unsigned sec_layer;
235         ADS_STATUS status;
236         krb5_principal principal;
237         krb5_context ctx;
238         krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
239         gss_OID_desc nt_principal = 
240         {10, "\052\206\110\206\367\022\001\002\002\002"};
241
242         /* we need to fetch a service ticket as the ldap user in the
243            servers realm, regardless of our realm */
244         asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);
245         krb5_init_context(&ctx);
246         krb5_set_default_tgs_ktypes(ctx, enc_types);
247         krb5_parse_name(ctx, sname, &principal);
248         free(sname);
249         krb5_free_context(ctx); 
250
251         input_name.value = &principal;
252         input_name.length = sizeof(principal);
253
254         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
255         if (gss_rc) {
256                 return ADS_ERROR_GSS(gss_rc, minor_status);
257         }
258
259         context_handle = GSS_C_NO_CONTEXT;
260
261         input_token.value = NULL;
262         input_token.length = 0;
263
264         for (i=0; i < MAX_GSS_PASSES; i++) {
265                 gss_rc = gss_init_sec_context(&minor_status,
266                                           GSS_C_NO_CREDENTIAL,
267                                           &context_handle,
268                                           serv_name,
269                                           mech_type,
270                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
271                                           0,
272                                           NULL,
273                                           &input_token,
274                                           NULL,
275                                           &output_token,
276                                           &ret_flags,
277                                           NULL);
278
279                 if (input_token.value) {
280                         gss_release_buffer(&minor_status, &input_token);
281                 }
282
283                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
284                         status = ADS_ERROR_GSS(gss_rc, minor_status);
285                         goto failed;
286                 }
287
288                 cred.bv_val = output_token.value;
289                 cred.bv_len = output_token.length;
290
291                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
292                                       &scred);
293                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
294                         status = ADS_ERROR(rc);
295                         goto failed;
296                 }
297
298                 if (output_token.value) {
299                         gss_release_buffer(&minor_status, &output_token);
300                 }
301
302                 if (scred) {
303                         input_token.value = scred->bv_val;
304                         input_token.length = scred->bv_len;
305                 } else {
306                         input_token.value = NULL;
307                         input_token.length = 0;
308                 }
309
310                 if (gss_rc == 0) break;
311         }
312
313         gss_release_name(&minor_status, &serv_name);
314
315         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
316                             &conf_state,NULL);
317         if (gss_rc) {
318                 status = ADS_ERROR_GSS(gss_rc, minor_status);
319                 goto failed;
320         }
321
322         gss_release_buffer(&minor_status, &input_token);
323
324         p = (uint8 *)output_token.value;
325
326         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
327
328         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
329         sec_layer = *p;
330
331         gss_release_buffer(&minor_status, &output_token);
332
333         output_token.value = malloc(strlen(ads->config.bind_path) + 8);
334         p = output_token.value;
335
336         *p++ = 1; /* no sign & seal selection */
337         /* choose the same size as the server gave us */
338         *p++ = max_msg_size>>16;
339         *p++ = max_msg_size>>8;
340         *p++ = max_msg_size;
341         snprintf(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
342         p += strlen(p);
343
344         output_token.length = PTR_DIFF(p, output_token.value);
345
346         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
347                           &output_token, &conf_state,
348                           &input_token);
349         if (gss_rc) {
350                 status = ADS_ERROR_GSS(gss_rc, minor_status);
351                 goto failed;
352         }
353
354         free(output_token.value);
355
356         cred.bv_val = input_token.value;
357         cred.bv_len = input_token.length;
358
359         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
360                               &scred);
361         status = ADS_ERROR(rc);
362
363         gss_release_buffer(&minor_status, &input_token);
364
365 failed:
366         return status;
367 }
368 #endif
369
370 /* mapping between SASL mechanisms and functions */
371 static struct {
372         const char *name;
373         ADS_STATUS (*fn)(ADS_STRUCT *);
374 } sasl_mechanisms[] = {
375         {"GSS-SPNEGO", ads_sasl_spnego_bind},
376 #ifdef HAVE_GSSAPI
377         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
378 #endif
379         {NULL, NULL}
380 };
381
382 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
383 {
384         const char *attrs[] = {"supportedSASLMechanisms", NULL};
385         char **values;
386         ADS_STATUS status;
387         int i, j;
388         void *res;
389
390         /* get a list of supported SASL mechanisms */
391         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
392         if (!ADS_ERR_OK(status)) return status;
393
394         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
395
396         /* try our supported mechanisms in order */
397         for (i=0;sasl_mechanisms[i].name;i++) {
398                 /* see if the server supports it */
399                 for (j=0;values && values[j];j++) {
400                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
401                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
402                                 status = sasl_mechanisms[i].fn(ads);
403                                 ldap_value_free(values);
404                                 ldap_msgfree(res);
405                                 return status;
406                         }
407                 }
408         }
409
410         ldap_value_free(values);
411         ldap_msgfree(res);
412         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
413 }
414
415 #endif
416