/src/samba/lib/addns/dnsgss.c
Line | Count | Source (jump to first uncovered line) |
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 "replace.h" |
26 | | #include <talloc.h> |
27 | | #include "lib/util/talloc_stack.h" |
28 | | #include "lib/util/data_blob.h" |
29 | | #include "lib/util/time.h" |
30 | | #include "lib/util/charset/charset.h" |
31 | | #include "libcli/util/ntstatus.h" |
32 | | #include "auth/gensec/gensec.h" |
33 | | |
34 | | #include "dns.h" |
35 | | |
36 | | static DNS_ERROR dns_negotiate_gss_ctx_int(struct dns_connection *conn, |
37 | | const char *keyname, |
38 | | struct gensec_security *gensec, |
39 | | enum dns_ServerType srv_type) |
40 | 0 | { |
41 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
42 | 0 | struct dns_request *req = NULL; |
43 | 0 | struct dns_buffer *buf = NULL; |
44 | 0 | DATA_BLOB in = { .length = 0, }; |
45 | 0 | DATA_BLOB out = { .length = 0, }; |
46 | 0 | NTSTATUS status; |
47 | 0 | DNS_ERROR err; |
48 | |
|
49 | 0 | do { |
50 | 0 | status = gensec_update(gensec, frame, in, &out); |
51 | 0 | data_blob_free(&in); |
52 | 0 | if (GENSEC_UPDATE_IS_NTERROR(status)) { |
53 | 0 | err = ERROR_DNS_GSS_ERROR; |
54 | 0 | goto error; |
55 | 0 | } |
56 | | |
57 | 0 | if (out.length != 0) { |
58 | 0 | struct dns_rrec *rec; |
59 | |
|
60 | 0 | time_t t = time(NULL); |
61 | |
|
62 | 0 | err = dns_create_query(frame, keyname, QTYPE_TKEY, |
63 | 0 | DNS_CLASS_IN, &req); |
64 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
65 | | |
66 | 0 | err = dns_create_tkey_record( |
67 | 0 | req, keyname, "gss.microsoft.com", t, |
68 | 0 | t + 86400, DNS_TKEY_MODE_GSSAPI, 0, |
69 | 0 | out.length, out.data, |
70 | 0 | &rec ); |
71 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
72 | | |
73 | | /* Windows 2000 DNS is broken and requires the |
74 | | TKEY payload in the Answer section instead |
75 | | of the Additional section like Windows 2003 */ |
76 | | |
77 | 0 | if ( srv_type == DNS_SRV_WIN2000 ) { |
78 | 0 | err = dns_add_rrec(req, rec, &req->num_answers, |
79 | 0 | &req->answers); |
80 | 0 | } else { |
81 | 0 | err = dns_add_rrec(req, rec, &req->num_additionals, |
82 | 0 | &req->additional); |
83 | 0 | } |
84 | | |
85 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
86 | | |
87 | 0 | err = dns_marshall_request(frame, req, &buf); |
88 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
89 | | |
90 | 0 | err = dns_send(conn, buf); |
91 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
92 | | |
93 | 0 | TALLOC_FREE(buf); |
94 | 0 | TALLOC_FREE(req); |
95 | |
|
96 | 0 | err = dns_receive(frame, conn, &buf); |
97 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
98 | 0 | } |
99 | | |
100 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { |
101 | 0 | struct dns_request *resp; |
102 | 0 | struct dns_tkey_record *tkey; |
103 | 0 | struct dns_rrec *tkey_answer = NULL; |
104 | 0 | uint16_t i; |
105 | |
|
106 | 0 | if (buf == NULL) { |
107 | 0 | err = ERROR_DNS_BAD_RESPONSE; |
108 | 0 | goto error; |
109 | 0 | } |
110 | | |
111 | 0 | err = dns_unmarshall_request(buf, buf, &resp); |
112 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
113 | | |
114 | | /* |
115 | | * TODO: Compare id and keyname |
116 | | */ |
117 | | |
118 | 0 | for (i=0; i < resp->num_answers; i++) { |
119 | 0 | if (resp->answers[i]->type != QTYPE_TKEY) { |
120 | 0 | continue; |
121 | 0 | } |
122 | | |
123 | 0 | tkey_answer = resp->answers[i]; |
124 | 0 | } |
125 | |
|
126 | 0 | if (tkey_answer == NULL) { |
127 | 0 | err = ERROR_DNS_INVALID_MESSAGE; |
128 | 0 | goto error; |
129 | 0 | } |
130 | | |
131 | 0 | err = dns_unmarshall_tkey_record( |
132 | 0 | frame, resp->answers[0], &tkey); |
133 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
134 | | |
135 | 0 | in = data_blob_const(tkey->key, tkey->key_length); |
136 | |
|
137 | 0 | TALLOC_FREE(buf); |
138 | 0 | } |
139 | |
|
140 | 0 | } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)); |
141 | | |
142 | | /* If we arrive here, we have a valid security context */ |
143 | | |
144 | 0 | err = ERROR_DNS_SUCCESS; |
145 | |
|
146 | 0 | error: |
147 | |
|
148 | 0 | TALLOC_FREE(frame); |
149 | 0 | return err; |
150 | 0 | } |
151 | | |
152 | | DNS_ERROR dns_negotiate_sec_ctx(const char *servername, |
153 | | const char *keyname, |
154 | | struct gensec_security *gensec, |
155 | | enum dns_ServerType srv_type) |
156 | 0 | { |
157 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
158 | 0 | DNS_ERROR err; |
159 | 0 | struct dns_connection *conn = NULL; |
160 | |
|
161 | 0 | err = dns_open_connection( servername, DNS_TCP, frame, &conn ); |
162 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
163 | | |
164 | 0 | err = dns_negotiate_gss_ctx_int(conn, keyname, |
165 | 0 | gensec, |
166 | 0 | srv_type); |
167 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
168 | | |
169 | 0 | error: |
170 | 0 | TALLOC_FREE(frame); |
171 | |
|
172 | 0 | return err; |
173 | 0 | } |
174 | | |
175 | | DNS_ERROR dns_sign_update(struct dns_update_request *req, |
176 | | struct gensec_security *gensec, |
177 | | const char *keyname, |
178 | | const char *algorithmname, |
179 | | time_t time_signed, uint16_t fudge) |
180 | 0 | { |
181 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
182 | 0 | struct dns_buffer *buf; |
183 | 0 | DNS_ERROR err; |
184 | 0 | struct dns_domain_name *key, *algorithm; |
185 | 0 | struct dns_rrec *rec; |
186 | 0 | DATA_BLOB mic = { .length = 0, }; |
187 | 0 | NTSTATUS status; |
188 | |
|
189 | 0 | err = dns_marshall_update_request(frame, req, &buf); |
190 | 0 | if (!ERR_DNS_IS_OK(err)) return err; |
191 | | |
192 | 0 | err = dns_domain_name_from_string(frame, keyname, &key); |
193 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
194 | | |
195 | 0 | err = dns_domain_name_from_string(frame, algorithmname, &algorithm); |
196 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
197 | | |
198 | 0 | dns_marshall_domain_name(buf, key); |
199 | 0 | dns_marshall_uint16(buf, DNS_CLASS_ANY); |
200 | 0 | dns_marshall_uint32(buf, 0); /* TTL */ |
201 | 0 | dns_marshall_domain_name(buf, algorithm); |
202 | 0 | dns_marshall_uint16(buf, 0); /* Time prefix for 48-bit time_t */ |
203 | 0 | dns_marshall_uint32(buf, time_signed); |
204 | 0 | dns_marshall_uint16(buf, fudge); |
205 | 0 | dns_marshall_uint16(buf, 0); /* error */ |
206 | 0 | dns_marshall_uint16(buf, 0); /* other len */ |
207 | |
|
208 | 0 | err = buf->error; |
209 | 0 | if (!ERR_DNS_IS_OK(buf->error)) goto error; |
210 | | |
211 | 0 | status = gensec_sign_packet(gensec, |
212 | 0 | frame, |
213 | 0 | buf->data, |
214 | 0 | buf->offset, |
215 | 0 | buf->data, |
216 | 0 | buf->offset, |
217 | 0 | &mic); |
218 | 0 | if (!NT_STATUS_IS_OK(status)) { |
219 | 0 | err = ERROR_DNS_GSS_ERROR; |
220 | 0 | goto error; |
221 | 0 | } |
222 | | |
223 | 0 | if (mic.length > 0xffff) { |
224 | 0 | err = ERROR_DNS_GSS_ERROR; |
225 | 0 | goto error; |
226 | 0 | } |
227 | | |
228 | 0 | err = dns_create_tsig_record(frame, keyname, algorithmname, time_signed, |
229 | 0 | fudge, mic.length, mic.data, |
230 | 0 | req->id, 0, &rec); |
231 | 0 | if (!ERR_DNS_IS_OK(err)) goto error; |
232 | | |
233 | 0 | err = dns_add_rrec(req, rec, &req->num_additionals, &req->additional); |
234 | |
|
235 | 0 | error: |
236 | 0 | TALLOC_FREE(frame); |
237 | 0 | return err; |
238 | 0 | } |