smbtorture: Directory Leases vs overwrite
[samba4-gss.git] / source4 / kdc / kpasswd-service.c
blobc671eb46d07027467a896667ed1e40d001a2f050
1 /*
2 Unix SMB/CIFS implementation.
4 Samba kpasswd implementation
6 Copyright (c) 2005 Andrew Bartlett <abartlet@samba.org>
7 Copyright (c) 2016 Andreas Schneider <asn@samba.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "samba/service_task.h"
25 #include "tsocket/tsocket.h"
26 #include "auth/credentials/credentials.h"
27 #include "auth/auth.h"
28 #include "auth/gensec/gensec.h"
29 #include "kdc/kdc-server.h"
30 #include "kdc/kpasswd-service.h"
31 #include "kdc/kpasswd-helper.h"
32 #include "param/param.h"
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_KERBEROS
37 #define HEADER_LEN 6
38 #ifndef RFC3244_VERSION
39 #define RFC3244_VERSION 0xff80
40 #endif
42 kdc_code kpasswd_process(struct kdc_server *kdc,
43 TALLOC_CTX *mem_ctx,
44 DATA_BLOB *request,
45 DATA_BLOB *reply,
46 struct tsocket_address *remote_addr,
47 struct tsocket_address *local_addr,
48 int datagram)
50 uint16_t len;
51 uint16_t verno;
52 uint16_t ap_req_len;
53 uint16_t enc_data_len;
54 DATA_BLOB ap_req_blob = data_blob_null;
55 DATA_BLOB ap_rep_blob = data_blob_null;
56 DATA_BLOB enc_data_blob = data_blob_null;
57 DATA_BLOB dec_data_blob = data_blob_null;
58 DATA_BLOB kpasswd_dec_reply = data_blob_null;
59 const char *error_string = NULL;
60 krb5_error_code error_code = 0;
61 struct cli_credentials *server_credentials;
62 struct gensec_security *gensec_security;
63 #ifndef SAMBA4_USES_HEIMDAL
64 struct sockaddr_storage remote_ss;
65 #endif
66 struct sockaddr_storage local_ss;
67 ssize_t socklen;
68 TALLOC_CTX *tmp_ctx;
69 kdc_code rc = KDC_ERROR;
70 krb5_error_code code = 0;
71 NTSTATUS status;
72 int rv;
73 bool is_inet;
74 bool ok;
76 if (kdc->am_rodc) {
77 return KDC_PROXY_REQUEST;
80 tmp_ctx = talloc_new(mem_ctx);
81 if (tmp_ctx == NULL) {
82 return KDC_ERROR;
85 is_inet = tsocket_address_is_inet(remote_addr, "ip");
86 if (!is_inet) {
87 DBG_WARNING("Invalid remote IP address\n");
88 goto done;
91 #ifndef SAMBA4_USES_HEIMDAL
93 * FIXME: Heimdal fails to to do a krb5_rd_req() in gensec_krb5 if we
94 * set the remote address.
97 /* remote_addr */
98 socklen = tsocket_address_bsd_sockaddr(remote_addr,
99 (struct sockaddr *)&remote_ss,
100 sizeof(struct sockaddr_storage));
101 if (socklen < 0) {
102 DBG_WARNING("Invalid remote IP address\n");
103 goto done;
105 #endif
107 /* local_addr */
108 socklen = tsocket_address_bsd_sockaddr(local_addr,
109 (struct sockaddr *)&local_ss,
110 sizeof(struct sockaddr_storage));
111 if (socklen < 0) {
112 DBG_WARNING("Invalid local IP address\n");
113 goto done;
116 if (request->length <= HEADER_LEN) {
117 DBG_WARNING("Request truncated\n");
118 goto done;
121 len = RSVAL(request->data, 0);
122 if (request->length != len) {
123 DBG_WARNING("Request length does not match\n");
124 goto done;
127 verno = RSVAL(request->data, 2);
128 if (verno != 1 && verno != RFC3244_VERSION) {
129 DBG_WARNING("Unsupported version: 0x%04x\n", verno);
132 ap_req_len = RSVAL(request->data, 4);
133 if ((ap_req_len >= len) || ((ap_req_len + HEADER_LEN) >= len)) {
134 DBG_WARNING("AP_REQ truncated\n");
135 goto done;
138 ap_req_blob = data_blob_const(&request->data[HEADER_LEN], ap_req_len);
140 enc_data_len = len - ap_req_len;
141 enc_data_blob = data_blob_const(&request->data[HEADER_LEN + ap_req_len],
142 enc_data_len);
144 server_credentials = cli_credentials_init(tmp_ctx);
145 if (server_credentials == NULL) {
146 DBG_ERR("Failed to initialize server credentials!\n");
147 goto done;
151 * We want the credentials subsystem to use the krb5 context we already
152 * have, rather than a new context.
154 * On this context the KDB plugin has been loaded, so we can access
155 * dsdb.
157 status = cli_credentials_set_krb5_context(server_credentials,
158 kdc->smb_krb5_context);
159 if (!NT_STATUS_IS_OK(status)) {
160 goto done;
163 ok = cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
164 if (!ok) {
165 goto done;
169 * After calling cli_credentials_set_conf(), explicitly set the realm
170 * with CRED_SPECIFIED. We need to do this so the result of
171 * principal_from_credentials() called from the gensec layer is
172 * CRED_SPECIFIED rather than CRED_SMB_CONF, avoiding a fallback to
173 * match-by-key (very undesirable in this case).
175 ok = cli_credentials_set_realm(server_credentials,
176 lpcfg_realm(kdc->task->lp_ctx),
177 CRED_SPECIFIED);
178 if (!ok) {
179 goto done;
182 ok = cli_credentials_set_username(server_credentials,
183 "kadmin/changepw",
184 CRED_SPECIFIED);
185 if (!ok) {
186 goto done;
189 /* Check that the server principal is indeed CRED_SPECIFIED. */
191 char *principal = NULL;
192 enum credentials_obtained obtained;
194 principal = cli_credentials_get_principal_and_obtained(server_credentials,
195 tmp_ctx,
196 &obtained);
197 if (obtained < CRED_SPECIFIED) {
198 goto done;
201 TALLOC_FREE(principal);
204 rv = cli_credentials_set_keytab_name(server_credentials,
205 kdc->task->lp_ctx,
206 kdc->kpasswd_keytab_name,
207 CRED_SPECIFIED);
208 if (rv != 0) {
209 DBG_ERR("Failed to set credentials keytab name\n");
210 goto done;
213 status = samba_server_gensec_start(tmp_ctx,
214 kdc->task->event_ctx,
215 kdc->task->msg_ctx,
216 kdc->task->lp_ctx,
217 server_credentials,
218 "kpasswd",
219 &gensec_security);
220 if (!NT_STATUS_IS_OK(status)) {
221 goto done;
224 status = gensec_set_local_address(gensec_security, local_addr);
225 if (!NT_STATUS_IS_OK(status)) {
226 goto done;
229 #ifndef SAMBA4_USES_HEIMDAL
230 status = gensec_set_remote_address(gensec_security, remote_addr);
231 if (!NT_STATUS_IS_OK(status)) {
232 goto done;
234 #endif
236 /* We want the GENSEC wrap calls to generate PRIV tokens */
237 gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
239 /* Use the krb5 gesec mechanism so we can load DB modules */
240 status = gensec_start_mech_by_name(gensec_security, "krb5");
241 if (!NT_STATUS_IS_OK(status)) {
242 goto done;
246 * Accept the AP-REQ and generate the AP-REP we need for the reply
248 * We only allow KRB5 and make sure the backend to is RPC/IPC free.
250 * See gensec_krb5_update_internal() as GENSEC_SERVER.
252 * It allows gensec_update() not to block.
254 * If that changes in future we need to use
255 * gensec_update_send/recv here!
257 status = gensec_update(gensec_security, tmp_ctx,
258 ap_req_blob, &ap_rep_blob);
259 if (!NT_STATUS_IS_OK(status) &&
260 !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
261 ap_rep_blob = data_blob_null;
262 error_code = KRB5_KPASSWD_HARDERROR;
263 error_string = talloc_asprintf(tmp_ctx,
264 "gensec_update failed - %s\n",
265 nt_errstr(status));
266 DBG_ERR("%s", error_string);
267 goto reply;
270 status = gensec_unwrap(gensec_security,
271 tmp_ctx,
272 &enc_data_blob,
273 &dec_data_blob);
274 if (!NT_STATUS_IS_OK(status)) {
275 ap_rep_blob = data_blob_null;
276 error_code = KRB5_KPASSWD_HARDERROR;
277 error_string = talloc_asprintf(tmp_ctx,
278 "gensec_unwrap failed - %s\n",
279 nt_errstr(status));
280 DBG_ERR("%s", error_string);
281 goto reply;
284 code = kpasswd_handle_request(kdc,
285 tmp_ctx,
286 gensec_security,
287 verno,
288 &dec_data_blob,
289 &kpasswd_dec_reply,
290 &error_string);
291 if (code != 0) {
292 ap_rep_blob = data_blob_null;
293 error_code = code;
294 goto reply;
297 status = gensec_wrap(gensec_security,
298 tmp_ctx,
299 &kpasswd_dec_reply,
300 &enc_data_blob);
301 if (!NT_STATUS_IS_OK(status)) {
302 ap_rep_blob = data_blob_null;
303 error_code = KRB5_KPASSWD_HARDERROR;
304 error_string = talloc_asprintf(tmp_ctx,
305 "gensec_wrap failed - %s\n",
306 nt_errstr(status));
307 DBG_ERR("%s", error_string);
308 goto reply;
311 reply:
312 if (error_code != 0) {
313 krb5_data k_enc_data;
314 krb5_data k_dec_data;
315 const char *principal_string;
316 krb5_principal server_principal;
318 if (error_string == NULL) {
319 DBG_ERR("Invalid error string! This should not happen\n");
320 goto done;
323 ok = kpasswd_make_error_reply(tmp_ctx,
324 error_code,
325 error_string,
326 &dec_data_blob);
327 if (!ok) {
328 DBG_ERR("Failed to create error reply\n");
329 goto done;
332 k_dec_data = smb_krb5_data_from_blob(dec_data_blob);
334 principal_string = cli_credentials_get_principal(server_credentials,
335 tmp_ctx);
336 if (principal_string == NULL) {
337 goto done;
340 code = smb_krb5_parse_name(kdc->smb_krb5_context->krb5_context,
341 principal_string,
342 &server_principal);
343 if (code != 0) {
344 DBG_ERR("Failed to create principal: %s\n",
345 error_message(code));
346 goto done;
349 code = smb_krb5_mk_error(kdc->smb_krb5_context->krb5_context,
350 KRB5KDC_ERR_NONE + error_code,
351 NULL, /* e_text */
352 &k_dec_data,
353 NULL, /* client */
354 server_principal,
355 &k_enc_data);
356 krb5_free_principal(kdc->smb_krb5_context->krb5_context,
357 server_principal);
358 if (code != 0) {
359 DBG_ERR("Failed to create krb5 error reply: %s\n",
360 error_message(code));
361 goto done;
364 enc_data_blob = data_blob_talloc(tmp_ctx,
365 k_enc_data.data,
366 k_enc_data.length);
367 if (enc_data_blob.data == NULL) {
368 DBG_ERR("Failed to allocate memory for error reply\n");
369 goto done;
373 *reply = data_blob_talloc(mem_ctx,
374 NULL,
375 HEADER_LEN + ap_rep_blob.length + enc_data_blob.length);
376 if (reply->data == NULL) {
377 goto done;
379 RSSVAL(reply->data, 0, reply->length);
380 RSSVAL(reply->data, 2, 1);
381 RSSVAL(reply->data, 4, ap_rep_blob.length);
382 if (ap_rep_blob.data != NULL) {
383 memcpy(reply->data + HEADER_LEN,
384 ap_rep_blob.data,
385 ap_rep_blob.length);
387 memcpy(reply->data + HEADER_LEN + ap_rep_blob.length,
388 enc_data_blob.data,
389 enc_data_blob.length);
391 rc = KDC_OK;
392 done:
393 talloc_free(tmp_ctx);
394 return rc;