updated the 3.0 branch from the head branch - ready for alpha18
[bbaumbach/samba-autobuild/.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 #if USE_CYRUS_SASL
26 /*
27   this is a minimal interact function, just enough for SASL to talk
28   GSSAPI/kerberos to W2K
29   Error handling is a bit of a problem. I can't see how to get Cyrus-sasl
30   to give sensible errors
31 */
32 static int sasl_interact(LDAP *ld,unsigned flags,void *defaults,void *in)
33 {
34         sasl_interact_t *interact = in;
35
36         while (interact->id != SASL_CB_LIST_END) {
37                 interact->result = strdup("");
38                 interact->len = strlen(interact->result);
39                 interact++;
40         }
41         
42         return LDAP_SUCCESS;
43 }
44 #endif
45
46
47 #define MAX_GSS_PASSES 3
48
49 /* this performs a SASL/gssapi bind
50    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
51    is very dependent on correctly configured DNS whereas
52    this routine is much less fragile
53    see RFC2078 for details
54 */
55 ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
56 {
57         int minor_status;
58         gss_name_t serv_name;
59         gss_buffer_desc input_name;
60         gss_ctx_id_t context_handle;
61         gss_OID mech_type = GSS_C_NULL_OID;
62         gss_buffer_desc output_token, input_token;
63         OM_uint32 ret_flags, conf_state;
64         struct berval cred;
65         struct berval *scred;
66         int i=0;
67         int gss_rc, rc;
68         uint8 *p;
69         uint32 max_msg_size;
70         char *sname;
71         ADS_STATUS status;
72         krb5_principal principal;
73         krb5_context ctx;
74         krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL};
75         gss_OID_desc nt_principal = 
76         {10, "\052\206\110\206\367\022\001\002\002\002"};
77
78         /* we need to fetch a service ticket as the ldap user in the
79            servers realm, regardless of our realm */
80         asprintf(&sname, "ldap/%s@%s", ads->ldap_server_name, ads->server_realm);
81         krb5_init_context(&ctx);
82         krb5_set_default_tgs_ktypes(ctx, enc_types);
83         krb5_parse_name(ctx, sname, &principal);
84         free(sname);
85         krb5_free_context(ctx); 
86
87         input_name.value = &principal;
88         input_name.length = sizeof(principal);
89
90         gss_rc = gss_import_name(&minor_status,&input_name,&nt_principal, &serv_name);
91         if (gss_rc) {
92                 return ADS_ERROR_GSS(gss_rc, minor_status);
93         }
94
95         context_handle = GSS_C_NO_CONTEXT;
96
97         input_token.value = NULL;
98         input_token.length = 0;
99
100         for (i=0; i < MAX_GSS_PASSES; i++) {
101                 gss_rc = gss_init_sec_context(&minor_status,
102                                           GSS_C_NO_CREDENTIAL,
103                                           &context_handle,
104                                           serv_name,
105                                           mech_type,
106                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
107                                           0,
108                                           NULL,
109                                           &input_token,
110                                           NULL,
111                                           &output_token,
112                                           &ret_flags,
113                                           NULL);
114
115                 if (input_token.value) {
116                         gss_release_buffer(&minor_status, &input_token);
117                 }
118
119                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
120                         status = ADS_ERROR_GSS(gss_rc, minor_status);
121                         goto failed;
122                 }
123
124                 cred.bv_val = output_token.value;
125                 cred.bv_len = output_token.length;
126
127                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
128                                       &scred);
129                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
130                         status = ADS_ERROR(rc);
131                         goto failed;
132                 }
133
134                 if (output_token.value) {
135                         gss_release_buffer(&minor_status, &output_token);
136                 }
137
138                 if (scred) {
139                         input_token.value = scred->bv_val;
140                         input_token.length = scred->bv_len;
141                 } else {
142                         input_token.value = NULL;
143                         input_token.length = 0;
144                 }
145
146                 if (gss_rc == 0) break;
147         }
148
149         gss_release_name(&minor_status, &serv_name);
150
151         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
152                             &conf_state,NULL);
153         if (gss_rc) {
154                 status = ADS_ERROR_GSS(gss_rc, minor_status);
155                 goto failed;
156         }
157
158         gss_release_buffer(&minor_status, &input_token);
159
160         p = (uint8 *)output_token.value;
161
162         max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
163
164         gss_release_buffer(&minor_status, &output_token);
165
166         output_token.value = malloc(strlen(ads->bind_path) + 8);
167         p = output_token.value;
168
169         *p++ = 1; /* no sign or seal */
170         /* choose the same size as the server gave us */
171         *p++ = max_msg_size>>16;
172         *p++ = max_msg_size>>8;
173         *p++ = max_msg_size;
174         snprintf(p, strlen(ads->bind_path)+4, "dn:%s", ads->bind_path);
175
176         output_token.length = strlen(ads->bind_path) + 8;
177
178         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
179                           &output_token, &conf_state,
180                           &input_token);
181         if (gss_rc) {
182                 status = ADS_ERROR_GSS(gss_rc, minor_status);
183                 goto failed;
184         }
185
186         free(output_token.value);
187
188         cred.bv_val = input_token.value;
189         cred.bv_len = input_token.length;
190
191         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
192                               &scred);
193         status = ADS_ERROR(rc);
194
195         gss_release_buffer(&minor_status, &input_token);
196
197 failed:
198         return status;
199 }
200
201 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
202 {
203 #if USE_CYRUS_SASL
204         int rc;
205         rc = ldap_sasl_interactive_bind_s(ads->ld, NULL, NULL, NULL, NULL, 
206                                           LDAP_SASL_QUIET,
207                                           sasl_interact, NULL);
208         return ADS_ERROR(rc);
209 #else
210         return ads_sasl_gssapi_bind(ads);
211 #endif
212 }
213
214 #endif
215