2 Unix SMB/CIFS implementation.
4 Samba kpasswd implementation
6 Copyright (c) 2016 Andreas Schneider <asn@samba.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "samba/service_task.h"
24 #include "param/param.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "gensec_krb5_helpers.h"
28 #include "kdc/kdc-server.h"
29 #include "kdc/kpasswd_glue.h"
30 #include "kdc/kpasswd-service.h"
31 #include "kdc/kpasswd-helper.h"
32 #include "../lib/util/asn1.h"
35 #define DBGC_CLASS DBGC_KERBEROS
37 #define RFC3244_VERSION 0xff80
39 krb5_error_code
decode_krb5_setpw_req(const krb5_data
*code
,
40 krb5_data
**password_out
,
41 krb5_principal
*target_out
);
44 * A fallback for when MIT refuses to parse a setpw structure without the
45 * (optional) target principal and realm
47 static bool decode_krb5_setpw_req_simple(TALLOC_CTX
*mem_ctx
,
48 const DATA_BLOB
*decoded_data
,
49 DATA_BLOB
*clear_data
)
51 struct asn1_data
*asn1
= NULL
;
54 asn1
= asn1_init(mem_ctx
, 3);
59 ret
= asn1_load(asn1
, *decoded_data
);
64 ret
= asn1_start_tag(asn1
, ASN1_SEQUENCE(0));
68 ret
= asn1_start_tag(asn1
, ASN1_CONTEXT(0));
72 ret
= asn1_read_OctetString(asn1
, mem_ctx
, clear_data
);
77 ret
= asn1_end_tag(asn1
);
81 ret
= asn1_end_tag(asn1
);
89 static krb5_error_code
kpasswd_change_password(struct kdc_server
*kdc
,
91 const struct gensec_security
*gensec_security
,
92 struct auth_session_info
*session_info
,
94 DATA_BLOB
*kpasswd_reply
,
95 const char **error_string
)
98 NTSTATUS result
= NT_STATUS_UNSUCCESSFUL
;
99 enum samPwdChangeReason reject_reason
;
100 const char *reject_string
= NULL
;
101 struct samr_DomInfo1
*dominfo
;
106 * We're doing a password change (rather than a password set), so check
107 * that we were given an initial ticket.
109 ret
= gensec_krb5_initial_ticket(gensec_security
);
111 *error_string
= "Expected an initial ticket";
112 return KRB5_KPASSWD_INITIAL_FLAG_NEEDED
;
115 status
= samdb_kpasswd_change_password(mem_ctx
,
117 kdc
->task
->event_ctx
,
124 if (!NT_STATUS_IS_OK(status
)) {
125 ok
= kpasswd_make_error_reply(mem_ctx
,
126 KRB5_KPASSWD_ACCESSDENIED
,
130 *error_string
= "Failed to create reply";
131 return KRB5_KPASSWD_HARDERROR
;
133 /* We want to send an an authenticated packet. */
137 ok
= kpasswd_make_pwchange_reply(mem_ctx
,
143 *error_string
= "Failed to create reply";
144 return KRB5_KPASSWD_HARDERROR
;
150 static krb5_error_code
kpasswd_set_password(struct kdc_server
*kdc
,
152 const struct gensec_security
*gensec_security
,
153 struct auth_session_info
*session_info
,
154 DATA_BLOB
*decoded_data
,
155 DATA_BLOB
*kpasswd_reply
,
156 const char **error_string
)
158 krb5_context context
= kdc
->smb_krb5_context
->krb5_context
;
159 DATA_BLOB clear_data
;
160 krb5_data k_dec_data
;
161 krb5_data
*k_clear_data
= NULL
;
162 krb5_principal target_principal
= NULL
;
163 krb5_error_code code
;
165 char *target_realm
= NULL
;
166 char *target_name
= NULL
;
167 char *target_principal_string
= NULL
;
168 bool is_service_principal
= false;
170 size_t num_components
;
171 enum samPwdChangeReason reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
172 struct samr_DomInfo1
*dominfo
= NULL
;
175 k_dec_data
= smb_krb5_data_from_blob(*decoded_data
);
177 code
= decode_krb5_setpw_req(&k_dec_data
,
181 clear_data
.data
= (uint8_t *)k_clear_data
->data
;
182 clear_data
.length
= k_clear_data
->length
;
184 target_principal
= NULL
;
187 * The MIT decode failed, so fall back to trying the simple
188 * case, without target_principal.
190 ok
= decode_krb5_setpw_req_simple(mem_ctx
,
194 DBG_WARNING("decode_krb5_setpw_req failed: %s\n",
195 error_message(code
));
196 ok
= kpasswd_make_error_reply(mem_ctx
,
197 KRB5_KPASSWD_MALFORMED
,
198 "Failed to decode packet",
201 *error_string
= "Failed to create reply";
202 return KRB5_KPASSWD_HARDERROR
;
208 ok
= convert_string_talloc_handle(mem_ctx
,
209 lpcfg_iconv_handle(kdc
->task
->lp_ctx
),
214 (void **)&password
.data
,
216 if (k_clear_data
!= NULL
) {
217 krb5_free_data(context
, k_clear_data
);
220 DBG_WARNING("String conversion failed\n");
221 *error_string
= "String conversion failed";
222 return KRB5_KPASSWD_HARDERROR
;
225 if (target_principal
!= NULL
) {
226 target_realm
= smb_krb5_principal_get_realm(
227 mem_ctx
, context
, target_principal
);
228 code
= krb5_unparse_name_flags(context
,
230 KRB5_PRINCIPAL_UNPARSE_NO_REALM
,
233 DBG_WARNING("Failed to parse principal\n");
234 *error_string
= "String conversion failed";
235 return KRB5_KPASSWD_HARDERROR
;
239 if ((target_name
!= NULL
&& target_realm
== NULL
) ||
240 (target_name
== NULL
&& target_realm
!= NULL
)) {
241 krb5_free_principal(context
, target_principal
);
242 TALLOC_FREE(target_realm
);
243 SAFE_FREE(target_name
);
245 ok
= kpasswd_make_error_reply(mem_ctx
,
246 KRB5_KPASSWD_MALFORMED
,
247 "Realm and principal must be "
248 "both present, or neither "
252 *error_string
= "Failed to create reply";
253 return KRB5_KPASSWD_HARDERROR
;
258 if (target_name
!= NULL
&& target_realm
!= NULL
) {
259 TALLOC_FREE(target_realm
);
260 SAFE_FREE(target_name
);
262 krb5_free_principal(context
, target_principal
);
263 TALLOC_FREE(target_realm
);
264 SAFE_FREE(target_name
);
266 return kpasswd_change_password(kdc
,
275 num_components
= krb5_princ_size(context
, target_principal
);
276 if (num_components
>= 2) {
277 is_service_principal
= true;
278 code
= krb5_unparse_name_flags(context
,
280 KRB5_PRINCIPAL_UNPARSE_SHORT
,
281 &target_principal_string
);
283 code
= krb5_unparse_name(context
,
285 &target_principal_string
);
287 krb5_free_principal(context
, target_principal
);
289 ok
= kpasswd_make_error_reply(mem_ctx
,
290 KRB5_KPASSWD_MALFORMED
,
291 "Failed to parse principal",
294 *error_string
= "Failed to create reply";
295 return KRB5_KPASSWD_HARDERROR
;
299 status
= kpasswd_samdb_set_password(mem_ctx
,
300 kdc
->task
->event_ctx
,
303 is_service_principal
,
304 target_principal_string
,
308 if (!NT_STATUS_IS_OK(status
)) {
309 DBG_ERR("kpasswd_samdb_set_password failed - %s\n",
313 ok
= kpasswd_make_pwchange_reply(mem_ctx
,
319 *error_string
= "Failed to create reply";
320 return KRB5_KPASSWD_HARDERROR
;
326 krb5_error_code
kpasswd_handle_request(struct kdc_server
*kdc
,
328 struct gensec_security
*gensec_security
,
330 DATA_BLOB
*decoded_data
,
331 DATA_BLOB
*kpasswd_reply
,
332 const char **error_string
)
334 struct auth_session_info
*session_info
;
336 krb5_error_code code
;
338 status
= gensec_session_info(gensec_security
,
341 if (!NT_STATUS_IS_OK(status
)) {
342 *error_string
= talloc_asprintf(mem_ctx
,
343 "gensec_session_info failed - "
346 return KRB5_KPASSWD_HARDERROR
;
350 * Since the kpasswd service shares its keys with the krbtgt, we might
351 * have received a TGT rather than a kpasswd ticket. We need to check
352 * the ticket type to ensure that TGTs cannot be misused in this manner.
354 code
= kpasswd_check_non_tgt(session_info
,
357 DBG_WARNING("%s\n", *error_string
);
366 ok
= convert_string_talloc_handle(mem_ctx
,
367 lpcfg_iconv_handle(kdc
->task
->lp_ctx
),
370 (const char *)decoded_data
->data
,
371 decoded_data
->length
,
372 (void **)&password
.data
,
375 *error_string
= "String conversion failed!";
376 DBG_WARNING("%s\n", *error_string
);
377 return KRB5_KPASSWD_HARDERROR
;
380 return kpasswd_change_password(kdc
,
388 case RFC3244_VERSION
: {
389 return kpasswd_set_password(kdc
,
398 return KRB5_KPASSWD_BAD_VERSION
;