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/>.
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"
35 #define DBGC_CLASS DBGC_KERBEROS
38 #ifndef RFC3244_VERSION
39 #define RFC3244_VERSION 0xff80
42 kdc_code
kpasswd_process(struct kdc_server
*kdc
,
46 struct tsocket_address
*remote_addr
,
47 struct tsocket_address
*local_addr
,
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
;
66 struct sockaddr_storage local_ss
;
69 kdc_code rc
= KDC_ERROR
;
70 krb5_error_code code
= 0;
77 return KDC_PROXY_REQUEST
;
80 tmp_ctx
= talloc_new(mem_ctx
);
81 if (tmp_ctx
== NULL
) {
85 is_inet
= tsocket_address_is_inet(remote_addr
, "ip");
87 DBG_WARNING("Invalid remote IP address\n");
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.
98 socklen
= tsocket_address_bsd_sockaddr(remote_addr
,
99 (struct sockaddr
*)&remote_ss
,
100 sizeof(struct sockaddr_storage
));
102 DBG_WARNING("Invalid remote IP address\n");
108 socklen
= tsocket_address_bsd_sockaddr(local_addr
,
109 (struct sockaddr
*)&local_ss
,
110 sizeof(struct sockaddr_storage
));
112 DBG_WARNING("Invalid local IP address\n");
116 if (request
->length
<= HEADER_LEN
) {
117 DBG_WARNING("Request truncated\n");
121 len
= RSVAL(request
->data
, 0);
122 if (request
->length
!= len
) {
123 DBG_WARNING("Request length does not match\n");
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");
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
],
144 server_credentials
= cli_credentials_init(tmp_ctx
);
145 if (server_credentials
== NULL
) {
146 DBG_ERR("Failed to initialize server credentials!\n");
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
157 status
= cli_credentials_set_krb5_context(server_credentials
,
158 kdc
->smb_krb5_context
);
159 if (!NT_STATUS_IS_OK(status
)) {
163 ok
= cli_credentials_set_conf(server_credentials
, kdc
->task
->lp_ctx
);
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
),
182 ok
= cli_credentials_set_username(server_credentials
,
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
,
197 if (obtained
< CRED_SPECIFIED
) {
201 TALLOC_FREE(principal
);
204 rv
= cli_credentials_set_keytab_name(server_credentials
,
206 kdc
->kpasswd_keytab_name
,
209 DBG_ERR("Failed to set credentials keytab name\n");
213 status
= samba_server_gensec_start(tmp_ctx
,
214 kdc
->task
->event_ctx
,
220 if (!NT_STATUS_IS_OK(status
)) {
224 status
= gensec_set_local_address(gensec_security
, local_addr
);
225 if (!NT_STATUS_IS_OK(status
)) {
229 #ifndef SAMBA4_USES_HEIMDAL
230 status
= gensec_set_remote_address(gensec_security
, remote_addr
);
231 if (!NT_STATUS_IS_OK(status
)) {
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
)) {
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",
266 DBG_ERR("%s", error_string
);
270 status
= gensec_unwrap(gensec_security
,
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",
280 DBG_ERR("%s", error_string
);
284 code
= kpasswd_handle_request(kdc
,
292 ap_rep_blob
= data_blob_null
;
297 status
= gensec_wrap(gensec_security
,
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",
307 DBG_ERR("%s", error_string
);
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");
323 ok
= kpasswd_make_error_reply(tmp_ctx
,
328 DBG_ERR("Failed to create error reply\n");
332 k_dec_data
= smb_krb5_data_from_blob(dec_data_blob
);
334 principal_string
= cli_credentials_get_principal(server_credentials
,
336 if (principal_string
== NULL
) {
340 code
= smb_krb5_parse_name(kdc
->smb_krb5_context
->krb5_context
,
344 DBG_ERR("Failed to create principal: %s\n",
345 error_message(code
));
349 code
= smb_krb5_mk_error(kdc
->smb_krb5_context
->krb5_context
,
350 KRB5KDC_ERR_NONE
+ error_code
,
356 krb5_free_principal(kdc
->smb_krb5_context
->krb5_context
,
359 DBG_ERR("Failed to create krb5 error reply: %s\n",
360 error_message(code
));
364 enc_data_blob
= data_blob_talloc(tmp_ctx
,
367 if (enc_data_blob
.data
== NULL
) {
368 DBG_ERR("Failed to allocate memory for error reply\n");
373 *reply
= data_blob_talloc(mem_ctx
,
375 HEADER_LEN
+ ap_rep_blob
.length
+ enc_data_blob
.length
);
376 if (reply
->data
== NULL
) {
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
,
387 memcpy(reply
->data
+ HEADER_LEN
+ ap_rep_blob
.length
,
389 enc_data_blob
.length
);
393 talloc_free(tmp_ctx
);