r21851: Obvious typos...
[ira/wip.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, write to the Free Software
23   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24   02110-1301  USA
25 */
26
27 #include "dns.h"
28 #include <ctype.h>
29
30
31 #ifdef HAVE_GSSAPI_SUPPORT
32
33 /*********************************************************************
34 *********************************************************************/
35
36 static int strupr( char *szDomainName )
37 {
38         if ( !szDomainName ) {
39                 return ( 0 );
40         }
41         while ( *szDomainName != '\0' ) {
42                 *szDomainName = toupper( *szDomainName );
43                 szDomainName++;
44         }
45         return ( 0 );
46 }
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_SEQUENCE_FLAG | GSS_C_CONF_FLAG |
105                         GSS_C_INTEG_FLAG | GSS_C_DELEG_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
168                         err = dns_receive(mem_ctx, conn, &buf);
169                         if (!ERR_DNS_IS_OK(err)) goto error;
170
171                         err = dns_unmarshall_request(buf, buf, &resp);
172                         if (!ERR_DNS_IS_OK(err)) goto error;
173
174                         /*
175                          * TODO: Compare id and keyname
176                          */
177                         
178                         if ((resp->num_additionals != 1) ||
179                             (resp->num_answers == 0) ||
180                             (resp->answers[0]->type != QTYPE_TKEY)) {
181                                 err = ERROR_DNS_INVALID_MESSAGE;
182                                 goto error;
183                         }
184
185                         err = dns_unmarshall_tkey_record(
186                                 mem_ctx, resp->answers[0], &tkey);
187                         if (!ERR_DNS_IS_OK(err)) goto error;
188
189                         input_desc.length = tkey->key_length;
190                         input_desc.value = talloc_move(mem_ctx, &tkey->key);
191
192                         input_ptr = &input_desc;
193
194                         TALLOC_FREE(buf);
195                 }
196
197         } while ( major == GSS_S_CONTINUE_NEEDED );
198
199         /* If we arrive here, we have a valid security context */
200
201         err = ERROR_DNS_SUCCESS;
202
203       error:
204
205         return err;
206 }
207
208 DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
209                                  const char *servername,
210                                  const char *keyname,
211                                  gss_ctx_id_t *gss_ctx,
212                                  enum dns_ServerType srv_type )
213 {
214         OM_uint32 major, minor;
215
216         char *upcaserealm, *targetname;
217         DNS_ERROR err;
218
219         gss_buffer_desc input_name;
220         struct dns_connection *conn;
221
222         gss_name_t targ_name;
223
224         krb5_principal host_principal;
225         krb5_context krb_ctx = NULL;
226
227         gss_OID_desc nt_host_oid_desc =
228                 { 10, (char *)"\052\206\110\206\367\022\001\002\002\002" };
229
230         TALLOC_CTX *mem_ctx;
231
232         if (!(mem_ctx = talloc_init("dns_negotiate_sec_ctx"))) {
233                 return ERROR_DNS_NO_MEMORY;
234         }
235
236         err = dns_open_connection( servername, DNS_TCP, mem_ctx, &conn );
237         if (!ERR_DNS_IS_OK(err)) goto error;
238
239         if (!(upcaserealm = talloc_strdup(mem_ctx, target_realm))) {
240                 err = ERROR_DNS_NO_MEMORY;
241                 goto error;
242         }
243
244         strupr(upcaserealm);
245
246         if (!(targetname = talloc_asprintf(mem_ctx, "dns/%s@%s",
247                                            servername, upcaserealm))) {
248                 err = ERROR_DNS_NO_MEMORY;
249                 goto error;
250         }
251
252         krb5_init_context( &krb_ctx );
253         krb5_parse_name( krb_ctx, targetname, &host_principal );
254
255         /* don't free the principal until after you call
256            gss_release_name() or else you'll get a segv
257            as the krb5_copy_principal() does a structure 
258            copy and not a deep copy.    --jerry*/
259
260         input_name.value = &host_principal;
261         input_name.length = sizeof( host_principal );
262
263         major = gss_import_name( &minor, &input_name,
264                                  &nt_host_oid_desc, &targ_name );
265
266         if (major) {
267                 krb5_free_principal( krb_ctx, host_principal );
268                 krb5_free_context( krb_ctx );
269                 err = ERROR_DNS_GSS_ERROR;
270                 goto error;
271         }
272
273         err = dns_negotiate_gss_ctx_int(mem_ctx, conn, keyname, 
274                                         targ_name, gss_ctx, srv_type );
275         
276         gss_release_name( &minor, &targ_name );
277
278         /* now we can free the principal */
279
280         krb5_free_principal( krb_ctx, host_principal );
281         krb5_free_context( krb_ctx );
282
283  error:
284         TALLOC_FREE(mem_ctx);
285
286         return err;
287 }
288
289 DNS_ERROR dns_sign_update(struct dns_update_request *req,
290                           gss_ctx_id_t gss_ctx,
291                           const char *keyname,
292                           const char *algorithmname,
293                           time_t time_signed, uint16 fudge)
294 {
295         struct dns_buffer *buf;
296         DNS_ERROR err;
297         struct dns_domain_name *key, *algorithm;
298         struct gss_buffer_desc_struct msg, mic;
299         OM_uint32 major, minor;
300         struct dns_rrec *rec;
301
302         err = dns_marshall_update_request(req, req, &buf);
303         if (!ERR_DNS_IS_OK(err)) return err;
304
305         err = dns_domain_name_from_string(buf, keyname, &key);
306         if (!ERR_DNS_IS_OK(err)) goto error;
307
308         err = dns_domain_name_from_string(buf, algorithmname, &algorithm);
309         if (!ERR_DNS_IS_OK(err)) goto error;
310
311         dns_marshall_domain_name(buf, key);
312         dns_marshall_uint16(buf, DNS_CLASS_ANY);
313         dns_marshall_uint32(buf, 0); /* TTL */
314         dns_marshall_domain_name(buf, algorithm);
315         dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */
316         dns_marshall_uint32(buf, time_signed);
317         dns_marshall_uint16(buf, fudge);
318         dns_marshall_uint16(buf, 0); /* error */
319         dns_marshall_uint16(buf, 0); /* other len */
320
321         err = buf->error;
322         if (!ERR_DNS_IS_OK(buf->error)) goto error;
323
324         msg.value = (void *)buf->data;
325         msg.length = buf->offset;
326
327         major = gss_get_mic(&minor, gss_ctx, 0, &msg, &mic);
328         if (major != 0) {
329                 err = ERROR_DNS_GSS_ERROR;
330                 goto error;
331         }
332
333         if (mic.length > 0xffff) {
334                 gss_release_buffer(&minor, &mic);
335                 err = ERROR_DNS_GSS_ERROR;
336                 goto error;
337         }
338
339         err = dns_create_tsig_record(buf, keyname, algorithmname, time_signed,
340                                      fudge, mic.length, (uint8 *)mic.value,
341                                      req->id, 0, &rec);
342         gss_release_buffer(&minor, &mic);
343         if (!ERR_DNS_IS_OK(err)) goto error;
344
345         err = dns_add_rrec(req, rec, &req->num_additionals, &req->additionals);
346
347  error:
348         TALLOC_FREE(buf);
349         return err;
350 }
351
352 #endif  /* HAVE_GSSAPI_SUPPORT */