r19977: * Fix a crash in the secure DNS update code. Don't free
[Samba.git] / source / libaddns / dnsgss.c
blob3d1a038363d0445cc693553ab89dbb11873700e8
1 /*
2 Public Interface file for Linux DNS client library implementation
4 Copyright (C) 2006 Krishna Ganugapati <krishnag@centeris.com>
5 Copyright (C) 2006 Gerald Carter <jerry@samba.org>
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
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.
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.
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
27 #include "dns.h"
28 #include <ctype.h>
31 #ifdef HAVE_GSSAPI_SUPPORT
33 /*********************************************************************
34 *********************************************************************/
36 static int strupr( char *szDomainName )
38 if ( !szDomainName ) {
39 return ( 0 );
41 while ( *szDomainName != '\0' ) {
42 *szDomainName = toupper( *szDomainName );
43 szDomainName++;
45 return ( 0 );
48 /*********************************************************************
49 *********************************************************************/
51 static void display_status_1( const char *m, OM_uint32 code, int type )
53 OM_uint32 maj_stat, min_stat;
54 gss_buffer_desc msg;
55 OM_uint32 msg_ctx;
57 msg_ctx = 0;
58 while ( 1 ) {
59 maj_stat = gss_display_status( &min_stat, code,
60 type, GSS_C_NULL_OID,
61 &msg_ctx, &msg );
62 fprintf( stdout, "GSS-API error %s: %s\n", m,
63 ( char * ) msg.value );
64 ( void ) gss_release_buffer( &min_stat, &msg );
66 if ( !msg_ctx )
67 break;
71 /*********************************************************************
72 *********************************************************************/
74 void display_status( const char *msg, OM_uint32 maj_stat, OM_uint32 min_stat )
76 display_status_1( msg, maj_stat, GSS_C_GSS_CODE );
77 display_status_1( msg, min_stat, GSS_C_MECH_CODE );
80 static DNS_ERROR dns_negotiate_gss_ctx_int( TALLOC_CTX *mem_ctx,
81 struct dns_connection *conn,
82 const char *keyname,
83 const gss_name_t target_name,
84 gss_ctx_id_t *ctx )
86 struct gss_buffer_desc_struct input_desc, *input_ptr, output_desc;
87 OM_uint32 major, minor;
88 OM_uint32 ret_flags;
89 DNS_ERROR err;
91 gss_OID_desc krb5_oid_desc =
92 { 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
94 *ctx = GSS_C_NO_CONTEXT;
95 input_ptr = NULL;
97 do {
98 major = gss_init_sec_context(
99 &minor, NULL, ctx, target_name, &krb5_oid_desc,
100 GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG |
101 GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG |
102 GSS_C_INTEG_FLAG | GSS_C_DELEG_FLAG,
103 0, NULL, input_ptr, NULL, &output_desc,
104 &ret_flags, NULL );
106 if (input_ptr != NULL) {
107 TALLOC_FREE(input_desc.value);
110 if (output_desc.length != 0) {
112 struct dns_request *req;
113 struct dns_rrec *rec;
114 struct dns_buffer *buf;
116 time_t t = time(NULL);
118 err = dns_create_query(mem_ctx, keyname, QTYPE_TKEY,
119 DNS_CLASS_IN, &req);
120 if (!ERR_DNS_IS_OK(err)) goto error;
122 err = dns_create_tkey_record(
123 req, keyname, "gss.microsoft.com", t,
124 t + 86400, DNS_TKEY_MODE_GSSAPI, 0,
125 output_desc.length, (uint8 *)output_desc.value,
126 &rec);
127 if (!ERR_DNS_IS_OK(err)) goto error;
129 err = dns_add_rrec(req, rec, &req->num_additionals,
130 &req->additionals);
131 if (!ERR_DNS_IS_OK(err)) goto error;
133 err = dns_marshall_request(req, req, &buf);
134 if (!ERR_DNS_IS_OK(err)) goto error;
136 err = dns_send(conn, buf);
137 if (!ERR_DNS_IS_OK(err)) goto error;
139 TALLOC_FREE(req);
142 gss_release_buffer(&minor, &output_desc);
144 if ((major != GSS_S_COMPLETE) &&
145 (major != GSS_S_CONTINUE_NEEDED)) {
146 return ERROR_DNS_GSS_ERROR;
149 if (major == GSS_S_CONTINUE_NEEDED) {
151 struct dns_request *resp;
152 struct dns_buffer *buf;
153 struct dns_tkey_record *tkey;
155 err = dns_receive(mem_ctx, conn, &buf);
156 if (!ERR_DNS_IS_OK(err)) goto error;
158 err = dns_unmarshall_request(buf, buf, &resp);
159 if (!ERR_DNS_IS_OK(err)) goto error;
162 * TODO: Compare id and keyname
165 if ((resp->num_additionals != 1) ||
166 (resp->answers[0]->type != QTYPE_TKEY)) {
167 err = ERROR_DNS_INVALID_MESSAGE;
168 goto error;
171 err = dns_unmarshall_tkey_record(
172 mem_ctx, resp->answers[0], &tkey);
173 if (!ERR_DNS_IS_OK(err)) goto error;
175 input_desc.length = tkey->key_length;
176 input_desc.value = talloc_move(mem_ctx, &tkey->key);
178 input_ptr = &input_desc;
180 TALLOC_FREE(buf);
183 } while ( major == GSS_S_CONTINUE_NEEDED );
185 /* If we arrive here, we have a valid security context */
187 err = ERROR_DNS_SUCCESS;
189 error:
191 return err;
194 DNS_ERROR dns_negotiate_sec_ctx( const char *target_realm,
195 const char *servername,
196 const char *keyname,
197 gss_ctx_id_t *gss_ctx )
199 OM_uint32 major, minor;
201 char *upcaserealm, *targetname;
202 DNS_ERROR err;
204 gss_buffer_desc input_name;
205 struct dns_connection *conn;
207 gss_name_t targ_name;
209 krb5_principal host_principal;
210 krb5_context krb_ctx = NULL;
212 gss_OID_desc nt_host_oid_desc =
213 { 10, (char *)"\052\206\110\206\367\022\001\002\002\002" };
215 TALLOC_CTX *mem_ctx;
217 if (!(mem_ctx = talloc_init("dns_negotiate_sec_ctx"))) {
218 return ERROR_DNS_NO_MEMORY;
221 err = dns_open( servername, DNS_TCP, mem_ctx, &conn );
222 if (!ERR_DNS_IS_OK(err)) goto error;
224 if (!(upcaserealm = talloc_strdup(mem_ctx, target_realm))) {
225 err = ERROR_DNS_NO_MEMORY;
226 goto error;
229 strupr(upcaserealm);
231 if (!(targetname = talloc_asprintf(mem_ctx, "dns/%s@%s",
232 servername, upcaserealm))) {
233 err = ERROR_DNS_NO_MEMORY;
234 goto error;
237 krb5_init_context( &krb_ctx );
238 krb5_parse_name( krb_ctx, targetname, &host_principal );
240 input_name.value = &host_principal;
241 input_name.length = sizeof( host_principal );
243 major = gss_import_name( &minor, &input_name,
244 &nt_host_oid_desc, &targ_name );
246 if (major) {
247 krb5_free_principal( krb_ctx, host_principal );
248 krb5_free_context( krb_ctx );
249 err = ERROR_DNS_GSS_ERROR;
250 goto error;
253 err = dns_negotiate_gss_ctx_int(mem_ctx, conn, keyname, targ_name,
254 gss_ctx);
256 krb5_free_principal( krb_ctx, host_principal );
257 krb5_free_context( krb_ctx );
258 gss_release_name( &minor, &targ_name );
260 error:
261 TALLOC_FREE(mem_ctx);
263 return err;
266 DNS_ERROR dns_sign_update(struct dns_update_request *req,
267 gss_ctx_id_t gss_ctx,
268 const char *keyname,
269 const char *algorithmname,
270 time_t time_signed, uint16 fudge)
272 struct dns_buffer *buf;
273 DNS_ERROR err;
274 struct dns_domain_name *key, *algorithm;
275 struct gss_buffer_desc_struct msg, mic;
276 OM_uint32 major, minor;
277 struct dns_rrec *rec;
279 err = dns_marshall_update_request(req, req, &buf);
280 if (!ERR_DNS_IS_OK(err)) return err;
282 err = dns_domain_name_from_string(buf, keyname, &key);
283 if (!ERR_DNS_IS_OK(err)) goto error;
285 err = dns_domain_name_from_string(buf, algorithmname, &algorithm);
286 if (!ERR_DNS_IS_OK(err)) goto error;
288 dns_marshall_domain_name(buf, key);
289 dns_marshall_uint16(buf, DNS_CLASS_ANY);
290 dns_marshall_uint32(buf, 0); /* TTL */
291 dns_marshall_domain_name(buf, algorithm);
292 dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */
293 dns_marshall_uint32(buf, time_signed);
294 dns_marshall_uint16(buf, fudge);
295 dns_marshall_uint16(buf, 0); /* error */
296 dns_marshall_uint16(buf, 0); /* other len */
298 err = buf->error;
299 if (!ERR_DNS_IS_OK(buf->error)) goto error;
301 msg.value = (void *)buf->data;
302 msg.length = buf->offset;
304 major = gss_get_mic(&minor, gss_ctx, 0, &msg, &mic);
305 if (major != 0) {
306 err = ERROR_DNS_GSS_ERROR;
307 goto error;
310 if (mic.length > 0xffff) {
311 gss_release_buffer(&minor, &mic);
312 err = ERROR_DNS_GSS_ERROR;
313 goto error;
316 err = dns_create_tsig_record(buf, keyname, algorithmname, time_signed,
317 fudge, mic.length, (uint8 *)mic.value,
318 req->id, 0, &rec);
319 gss_release_buffer(&minor, &mic);
320 if (!ERR_DNS_IS_OK(err)) goto error;
322 err = dns_add_rrec(req, rec, &req->num_additionals, &req->additionals);
324 error:
325 TALLOC_FREE(buf);
326 return err;
329 #endif /* HAVE_GSSAPI_SUPPORT */