Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Samba kpasswd implementation
5 :
6 : Copyright (c) 2016 Andreas Schneider <asn@samba.org>
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : #include "includes.h"
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 :
33 12 : static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
34 : TALLOC_CTX *mem_ctx,
35 : const struct gensec_security *gensec_security,
36 : struct auth_session_info *session_info,
37 : DATA_BLOB *password,
38 : DATA_BLOB *kpasswd_reply,
39 : const char **error_string)
40 : {
41 : NTSTATUS status;
42 12 : NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
43 : enum samPwdChangeReason reject_reason;
44 12 : const char *reject_string = NULL;
45 : struct samr_DomInfo1 *dominfo;
46 : bool ok;
47 : int ret;
48 :
49 : /*
50 : * We're doing a password change (rather than a password set), so check
51 : * that we were given an initial ticket.
52 : */
53 12 : ret = gensec_krb5_initial_ticket(gensec_security);
54 12 : if (ret != 1) {
55 0 : *error_string = "Expected an initial ticket";
56 0 : return KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
57 : }
58 :
59 24 : status = samdb_kpasswd_change_password(mem_ctx,
60 12 : kdc->task->lp_ctx,
61 12 : kdc->task->event_ctx,
62 : session_info,
63 : password,
64 : &reject_reason,
65 : &dominfo,
66 : &reject_string,
67 : &result);
68 12 : if (!NT_STATUS_IS_OK(status)) {
69 0 : ok = kpasswd_make_error_reply(mem_ctx,
70 : KRB5_KPASSWD_ACCESSDENIED,
71 : reject_string,
72 : kpasswd_reply);
73 0 : if (!ok) {
74 0 : *error_string = "Failed to create reply";
75 0 : return KRB5_KPASSWD_HARDERROR;
76 : }
77 : /* We want to send an an authenticated packet. */
78 0 : return 0;
79 : }
80 :
81 12 : ok = kpasswd_make_pwchange_reply(mem_ctx,
82 : result,
83 : reject_reason,
84 : dominfo,
85 : kpasswd_reply);
86 12 : if (!ok) {
87 0 : *error_string = "Failed to create reply";
88 0 : return KRB5_KPASSWD_HARDERROR;
89 : }
90 :
91 12 : return 0;
92 : }
93 :
94 20 : static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
95 : TALLOC_CTX *mem_ctx,
96 : const struct gensec_security *gensec_security,
97 : struct auth_session_info *session_info,
98 : DATA_BLOB *decoded_data,
99 : DATA_BLOB *kpasswd_reply,
100 : const char **error_string)
101 : {
102 20 : krb5_context context = kdc->smb_krb5_context->krb5_context;
103 : krb5_error_code code;
104 : krb5_principal target_principal;
105 20 : ChangePasswdDataMS chpw = {};
106 20 : size_t chpw_len = 0;
107 20 : DATA_BLOB password = data_blob_null;
108 20 : enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR;
109 20 : struct samr_DomInfo1 *dominfo = NULL;
110 20 : char *target_principal_string = NULL;
111 20 : bool is_service_principal = false;
112 : NTSTATUS status;
113 : bool ok;
114 :
115 20 : code = decode_ChangePasswdDataMS(decoded_data->data,
116 : decoded_data->length,
117 : &chpw,
118 : &chpw_len);
119 20 : if (code != 0) {
120 0 : DBG_WARNING("decode_ChangePasswdDataMS failed\n");
121 0 : ok = kpasswd_make_error_reply(mem_ctx,
122 : KRB5_KPASSWD_MALFORMED,
123 : "Failed to decode packet",
124 : kpasswd_reply);
125 0 : if (!ok) {
126 0 : *error_string = "Failed to create reply";
127 0 : return KRB5_KPASSWD_HARDERROR;
128 : }
129 0 : return 0;
130 : }
131 :
132 40 : ok = convert_string_talloc_handle(mem_ctx,
133 20 : lpcfg_iconv_handle(kdc->task->lp_ctx),
134 : CH_UTF8,
135 : CH_UTF16,
136 20 : (const char *)chpw.newpasswd.data,
137 : chpw.newpasswd.length,
138 : (void **)&password.data,
139 : &password.length);
140 20 : if (!ok) {
141 0 : free_ChangePasswdDataMS(&chpw);
142 0 : DBG_WARNING("String conversion failed\n");
143 0 : *error_string = "String conversion failed";
144 0 : return KRB5_KPASSWD_HARDERROR;
145 : }
146 :
147 40 : if ((chpw.targname != NULL && chpw.targrealm == NULL) ||
148 20 : (chpw.targname == NULL && chpw.targrealm != NULL)) {
149 0 : free_ChangePasswdDataMS(&chpw);
150 0 : ok = kpasswd_make_error_reply(mem_ctx,
151 : KRB5_KPASSWD_MALFORMED,
152 : "Realm and principal must be "
153 : "both present, or neither present",
154 : kpasswd_reply);
155 0 : if (!ok) {
156 0 : *error_string = "Failed to create reply";
157 0 : return KRB5_KPASSWD_HARDERROR;
158 : }
159 0 : return 0;
160 : }
161 :
162 20 : if (chpw.targname == NULL || chpw.targrealm == NULL) {
163 0 : free_ChangePasswdDataMS(&chpw);
164 0 : return kpasswd_change_password(kdc,
165 : mem_ctx,
166 : gensec_security,
167 : session_info,
168 : &password,
169 : kpasswd_reply,
170 : error_string);
171 : }
172 40 : code = krb5_build_principal_ext(context,
173 : &target_principal,
174 20 : strlen(*chpw.targrealm),
175 20 : *chpw.targrealm,
176 : 0);
177 20 : if (code != 0) {
178 0 : free_ChangePasswdDataMS(&chpw);
179 0 : return kpasswd_make_error_reply(mem_ctx,
180 : KRB5_KPASSWD_MALFORMED,
181 : "Failed to parse principal",
182 : kpasswd_reply);
183 : }
184 20 : code = copy_PrincipalName(chpw.targname,
185 20 : &target_principal->name);
186 20 : free_ChangePasswdDataMS(&chpw);
187 20 : if (code != 0) {
188 0 : krb5_free_principal(context, target_principal);
189 0 : return kpasswd_make_error_reply(mem_ctx,
190 : KRB5_KPASSWD_MALFORMED,
191 : "Failed to parse principal",
192 : kpasswd_reply);
193 : }
194 :
195 20 : if (target_principal->name.name_string.len >= 2) {
196 3 : is_service_principal = true;
197 :
198 3 : code = krb5_unparse_name_short(context,
199 : target_principal,
200 : &target_principal_string);
201 : } else {
202 17 : code = krb5_unparse_name(context,
203 : target_principal,
204 : &target_principal_string);
205 : }
206 20 : krb5_free_principal(context, target_principal);
207 20 : if (code != 0) {
208 0 : ok = kpasswd_make_error_reply(mem_ctx,
209 : KRB5_KPASSWD_MALFORMED,
210 : "Failed to parse principal",
211 : kpasswd_reply);
212 0 : if (!ok) {
213 0 : *error_string = "Failed to create reply";
214 0 : return KRB5_KPASSWD_HARDERROR;
215 : }
216 : }
217 :
218 60 : status = kpasswd_samdb_set_password(mem_ctx,
219 20 : kdc->task->event_ctx,
220 20 : kdc->task->lp_ctx,
221 : session_info,
222 : is_service_principal,
223 : target_principal_string,
224 : &password,
225 : &reject_reason,
226 : &dominfo);
227 20 : if (!NT_STATUS_IS_OK(status)) {
228 11 : DBG_ERR("kpasswd_samdb_set_password failed - %s\n",
229 : nt_errstr(status));
230 : }
231 :
232 20 : ok = kpasswd_make_pwchange_reply(mem_ctx,
233 : status,
234 : reject_reason,
235 : dominfo,
236 : kpasswd_reply);
237 20 : if (!ok) {
238 0 : *error_string = "Failed to create reply";
239 0 : return KRB5_KPASSWD_HARDERROR;
240 : }
241 :
242 20 : return 0;
243 : }
244 :
245 32 : krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
246 : TALLOC_CTX *mem_ctx,
247 : struct gensec_security *gensec_security,
248 : uint16_t verno,
249 : DATA_BLOB *decoded_data,
250 : DATA_BLOB *kpasswd_reply,
251 : const char **error_string)
252 : {
253 : struct auth_session_info *session_info;
254 : NTSTATUS status;
255 : krb5_error_code code;
256 :
257 32 : status = gensec_session_info(gensec_security,
258 : mem_ctx,
259 : &session_info);
260 32 : if (!NT_STATUS_IS_OK(status)) {
261 0 : *error_string = talloc_asprintf(mem_ctx,
262 : "gensec_session_info failed - %s",
263 : nt_errstr(status));
264 0 : return KRB5_KPASSWD_HARDERROR;
265 : }
266 :
267 : /*
268 : * Since the kpasswd service shares its keys with the krbtgt, we might
269 : * have received a TGT rather than a kpasswd ticket. We need to check
270 : * the ticket type to ensure that TGTs cannot be misused in this manner.
271 : */
272 32 : code = kpasswd_check_non_tgt(session_info,
273 : error_string);
274 32 : if (code != 0) {
275 0 : DBG_WARNING("%s\n", *error_string);
276 0 : return code;
277 : }
278 :
279 32 : switch(verno) {
280 12 : case KRB5_KPASSWD_VERS_CHANGEPW: {
281 12 : DATA_BLOB password = data_blob_null;
282 : bool ok;
283 :
284 24 : ok = convert_string_talloc_handle(mem_ctx,
285 12 : lpcfg_iconv_handle(kdc->task->lp_ctx),
286 : CH_UTF8,
287 : CH_UTF16,
288 12 : (const char *)decoded_data->data,
289 : decoded_data->length,
290 : (void **)&password.data,
291 : &password.length);
292 12 : if (!ok) {
293 0 : *error_string = "String conversion failed!";
294 0 : DBG_WARNING("%s\n", *error_string);
295 0 : return KRB5_KPASSWD_HARDERROR;
296 : }
297 :
298 12 : return kpasswd_change_password(kdc,
299 : mem_ctx,
300 : gensec_security,
301 : session_info,
302 : &password,
303 : kpasswd_reply,
304 : error_string);
305 : }
306 20 : case KRB5_KPASSWD_VERS_SETPW: {
307 20 : return kpasswd_set_password(kdc,
308 : mem_ctx,
309 : gensec_security,
310 : session_info,
311 : decoded_data,
312 : kpasswd_reply,
313 : error_string);
314 : }
315 0 : default:
316 0 : *error_string = talloc_asprintf(mem_ctx,
317 : "Protocol version %u not supported",
318 : verno);
319 0 : return KRB5_KPASSWD_BAD_VERSION;
320 : }
321 :
322 : return 0;
323 : }
|