source3/libaddns: don't depend on the order in resp->answers[]
[samba.git] / source3 / libaddns / dnsgss.c
1 /*
2   Public Interface file for Linux DNS client library implementation
3
4   Copyright (C) 2006 Krishna Ganugapati <krishnag@centeris.com>
5   Copyright (C) 2006 Gerald Carter <jerry@samba.org>
6
7      ** NOTE! The following LGPL license applies to the libaddns
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10
11   This library is free software; you can redistribute it and/or
12   modify it under the terms of the GNU Lesser General Public
13   License as published by the Free Software Foundation; either
14   version 2.1 of the License, or (at your option) any later version.
15
16   This library is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public
22   License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "dns.h"
26 #include <ctype.h>
27
28
29 #ifdef HAVE_GSSAPI_SUPPORT
30
31 /*********************************************************************
32 *********************************************************************/
33
34 #ifndef HAVE_STRUPR
35 static int strupr( char *szDomainName )
36 {
37         if ( !szDomainName ) {
38                 return ( 0 );
39         }
40         while ( *szDomainName != '\0' ) {
41                 *szDomainName = toupper( *szDomainName );
42                 szDomainName++;
43         }
44         return ( 0 );
45 }
46 #endif
47
48 #if 0
49 /*********************************************************************
50 *********************************************************************/
51
52 static void display_status_1( const char *m, OM_uint32 code, int type )
53 {
54         OM_uint32 maj_stat, min_stat;
55         gss_buffer_desc msg;
56         OM_uint32 msg_ctx;
57
58         msg_ctx = 0;
59         while ( 1 ) {
60                 maj_stat = gss_display_status( &min_stat, code,
61                                                type, GSS_C_NULL_OID,
62                                                &msg_ctx, &msg );
63                 fprintf( stdout, "GSS-API error %s: %s\n", m,
64                          ( char * ) msg.value );
65                 ( void ) gss_release_buffer( &min_stat, &msg );
66
67                 if ( !msg_ctx )
68                         break;
69         }
70 }
71
72 /*********************************************************************
73 *********************************************************************/
74
75 void display_status( const char *msg, OM_uint32 maj_stat, OM_uint32 min_stat )
76 {
77         display_status_1( msg, maj_stat, GSS_C_GSS_CODE );
78         display_status_1( msg, min_stat, GSS_C_MECH_CODE );
79 }
80 #endif
81
82 static DNS_ERROR dns_negotiate_gss_ctx_int( TALLOC_CTX *mem_ctx,
83                                             struct dns_connection *conn,
84                                             const char *keyname,
85                                             const gss_name_t target_name,
86                                             gss_ctx_id_t *ctx, 
87                                             enum dns_ServerType srv_type )
88 {
89         struct gss_buffer_desc_struct input_desc, *input_ptr, output_desc;
90         OM_uint32 major, minor;
91         OM_uint32 ret_flags;
92         DNS_ERROR err;
93
94         gss_OID_desc krb5_oid_desc =
95                 { 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
96
97         *ctx = GSS_C_NO_CONTEXT;
98         input_ptr = NULL;
99
100         do {
101                 major = gss_init_sec_context(
102                         &minor, NULL, ctx, target_name, &krb5_oid_desc,
103                         GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG |
104                         GSS_C_CONF_FLAG |
105                         GSS_C_INTEG_FLAG,
106                         0, NULL, input_ptr, NULL, &output_desc,
107                         &ret_flags, NULL );
108
109                 if (input_ptr != NULL) {
110                         TALLOC_FREE(input_desc.value);
111                 }
112
113                 if (output_desc.length != 0) {
114
115                         struct dns_request *req;
116                         struct dns_rrec *rec;
117                         struct dns_buffer *buf;
118
119                         time_t t = time(NULL);
120
121                         err = dns_create_query(mem_ctx, keyname, QTYPE_TKEY,
122                                                DNS_CLASS_IN, &req);
123                         if (!ERR_DNS_IS_OK(err)) goto error;
124
125                         err = dns_create_tkey_record(
126                                 req, keyname, "gss.microsoft.com", t,
127                                 t + 86400, DNS_TKEY_MODE_GSSAPI, 0,
128                                 output_desc.length, (uint8 *)output_desc.value,
129                                 &rec );
130                         if (!ERR_DNS_IS_OK(err)) goto error;
131
132                         /* Windows 2000 DNS is broken and requires the
133                            TKEY payload in the Answer section instead
134                            of the Additional seciton like Windows 2003 */
135
136                         if ( srv_type == DNS_SRV_WIN2000 ) {
137                                 err = dns_add_rrec(req, rec, &req->num_answers,
138                                                    &req->answers);
139                         } else {
140                                 err = dns_add_rrec(req, rec, &req->num_additionals,
141                                                    &req->additionals);
142                         }
143                         
144                         if (!ERR_DNS_IS_OK(err)) goto error;
145
146                         err = dns_marshall_request(req, req, &buf);
147                         if (!ERR_DNS_IS_OK(err)) goto error;
148
149                         err = dns_send(conn, buf);
150                         if (!ERR_DNS_IS_OK(err)) goto error;
151
152                         TALLOC_FREE(req);
153                 }
154
155                 gss_release_buffer(&minor, &output_desc);
156
157                 if ((major != GSS_S_COMPLETE) &&
158                     (major != GSS_S_CONTINUE_NEEDED)) {
159                         return ERROR_DNS_GSS_ERROR;
160                 }
161
162                 if (major == GSS_S_CONTINUE_NEEDED) {
163
164                         struct dns_request *resp;
165                         struct dns_buffer *buf;
166                         struct dns_tkey_record *tkey;
167                         struct dns_rrec *tkey_answer = NULL;
168                         uint16_t i;
169
170                         err = dns_receive(mem_ctx, conn, &buf);
171                         if (!ERR_DNS_IS_OK(err)) goto error;
172
173                         err = dns_unmarshall_request(buf, buf, &resp);
174                         if (!ERR_DNS_IS_OK(err)) goto error;
175
176                         /*
177                          * TODO: Compare id and keyname
178                          */
179
180                         for (i=0; i < resp->num_answers; i++) {
181                                 if (resp->answers[i]->type != QTYPE_TKEY) {
182                                         continue;
183                                 }
184
185                                 tkey_answer = resp->answers[i];
186                         }
187
188                         if (tkey_answer == NULL) {
189                                 err = ERROR_DNS_INVALID_MESSAGE;
190                                 goto error;
191                         }
192
193                         err = dns_unmarshall_tkey_record(
194                                 mem_ctx, resp->answers[0], &tkey);
195                         if (!ERR_DNS_IS_OK(err)) goto error;
196
197                         input_desc.length = tkey->key_length;
198                         input_desc.value = talloc_move(mem_ctx, &tkey->key);
199
200                         input_ptr = &input_desc;
201
202                         TALLOC_FREE(buf);
203                 }
204
205         } while ( major == GSS_S_CONTINUE_NEEDED );
206
207         /* If we arrive here, we have a valid security context */
208
209         err = ERROR_DNS_SUCCESS;
210
211       error:
212
213         return err;
214 }
215
216 DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
217                                  const char *servername,
218                                  const char *keyname,
219                                  gss_ctx_id_t *gss_ctx,
220                                  enum dns_ServerType srv_type )
221 {
222         OM_uint32 major, minor;
223
224         char *upcaserealm, *targetname;
225         DNS_ERROR err;
226
227         gss_buffer_desc input_name;
228         struct dns_connection *conn;
229
230         gss_name_t targ_name;
231
232         gss_OID_desc nt_host_oid_desc =
233                 {10, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
234
235         TALLOC_CTX *mem_ctx;
236
237         if (!(mem_ctx = talloc_init("dns_negotiate_sec_ctx"))) {
238                 return ERROR_DNS_NO_MEMORY;
239         }
240
241         err = dns_open_connection( servername, DNS_TCP, mem_ctx, &conn );
242         if (!ERR_DNS_IS_OK(err)) goto error;
243
244         if (!(upcaserealm = talloc_strdup(mem_ctx, target_realm))) {
245                 err = ERROR_DNS_NO_MEMORY;
246                 goto error;
247         }
248
249         strupr(upcaserealm);
250
251         if (!(targetname = talloc_asprintf(mem_ctx, "dns/%s@%s",
252                                            servername, upcaserealm))) {
253                 err = ERROR_DNS_NO_MEMORY;
254                 goto error;
255         }
256
257         input_name.value = targetname;
258         input_name.length = strlen(targetname);
259
260         major = gss_import_name( &minor, &input_name,
261                                  &nt_host_oid_desc, &targ_name );
262
263         if (major) {
264                 err = ERROR_DNS_GSS_ERROR;
265                 goto error;
266         }
267
268         err = dns_negotiate_gss_ctx_int(mem_ctx, conn, keyname, 
269                                         targ_name, gss_ctx, srv_type );
270         
271         gss_release_name( &minor, &targ_name );
272
273  error:
274         TALLOC_FREE(mem_ctx);
275
276         return err;
277 }
278
279 DNS_ERROR dns_sign_update(struct dns_update_request *req,
280                           gss_ctx_id_t gss_ctx,
281                           const char *keyname,
282                           const char *algorithmname,
283                           time_t time_signed, uint16 fudge)
284 {
285         struct dns_buffer *buf;
286         DNS_ERROR err;
287         struct dns_domain_name *key, *algorithm;
288         struct gss_buffer_desc_struct msg, mic;
289         OM_uint32 major, minor;
290         struct dns_rrec *rec;
291
292         err = dns_marshall_update_request(req, req, &buf);
293         if (!ERR_DNS_IS_OK(err)) return err;
294
295         err = dns_domain_name_from_string(buf, keyname, &key);
296         if (!ERR_DNS_IS_OK(err)) goto error;
297
298         err = dns_domain_name_from_string(buf, algorithmname, &algorithm);
299         if (!ERR_DNS_IS_OK(err)) goto error;
300
301         dns_marshall_domain_name(buf, key);
302         dns_marshall_uint16(buf, DNS_CLASS_ANY);
303         dns_marshall_uint32(buf, 0); /* TTL */
304         dns_marshall_domain_name(buf, algorithm);
305         dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */
306         dns_marshall_uint32(buf, time_signed);
307         dns_marshall_uint16(buf, fudge);
308         dns_marshall_uint16(buf, 0); /* error */
309         dns_marshall_uint16(buf, 0); /* other len */
310
311         err = buf->error;
312         if (!ERR_DNS_IS_OK(buf->error)) goto error;
313
314         msg.value = (void *)buf->data;
315         msg.length = buf->offset;
316
317         major = gss_get_mic(&minor, gss_ctx, 0, &msg, &mic);
318         if (major != 0) {
319                 err = ERROR_DNS_GSS_ERROR;
320                 goto error;
321         }
322
323         if (mic.length > 0xffff) {
324                 gss_release_buffer(&minor, &mic);
325                 err = ERROR_DNS_GSS_ERROR;
326                 goto error;
327         }
328
329         err = dns_create_tsig_record(buf, keyname, algorithmname, time_signed,
330                                      fudge, mic.length, (uint8 *)mic.value,
331                                      req->id, 0, &rec);
332         gss_release_buffer(&minor, &mic);
333         if (!ERR_DNS_IS_OK(err)) goto error;
334
335         err = dns_add_rrec(req, rec, &req->num_additionals, &req->additionals);
336
337  error:
338         TALLOC_FREE(buf);
339         return err;
340 }
341
342 #endif  /* HAVE_GSSAPI_SUPPORT */