• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/kdc/
1/*
2   Unix SMB/CIFS implementation.
3
4   kpasswd Server implementation
5
6   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7   Copyright (C) Andrew Tridgell	2005
8
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.
13
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.
18
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/>.
21*/
22
23#include "includes.h"
24#include "smbd/service_task.h"
25#include "lib/events/events.h"
26#include "lib/socket/socket.h"
27#include "system/network.h"
28#include "../lib/util/dlinklist.h"
29#include "lib/ldb/include/ldb.h"
30#include "auth/gensec/gensec.h"
31#include "auth/credentials/credentials.h"
32#include "auth/credentials/credentials_krb5.h"
33#include "auth/auth.h"
34#include "dsdb/samdb/samdb.h"
35#include "rpc_server/dcerpc_server.h"
36#include "rpc_server/samr/proto.h"
37#include "libcli/security/security.h"
38#include "param/param.h"
39#include "kdc/kdc.h"
40
41/* TODO: remove all SAMBA4_INTERNAL_HEIMDAL stuff from this file */
42#ifdef SAMBA4_INTERNAL_HEIMDAL
43#include "heimdal_build/kpasswdd-glue.h"
44#endif
45
46/* hold information about one kdc socket */
47struct kpasswd_socket {
48	struct socket_context *sock;
49	struct kdc_server *kdc;
50	struct tevent_fd *fde;
51
52	/* a queue of outgoing replies that have been deferred */
53	struct kdc_reply *send_queue;
54};
55
56/* Return true if there is a valid error packet formed in the error_blob */
57static bool kpasswdd_make_error_reply(struct kdc_server *kdc,
58				     TALLOC_CTX *mem_ctx,
59				     uint16_t result_code,
60				     const char *error_string,
61				     DATA_BLOB *error_blob)
62{
63	char *error_string_utf8;
64	size_t len;
65
66	DEBUG(result_code ? 3 : 10, ("kpasswdd: %s\n", error_string));
67
68	if (!push_utf8_talloc(mem_ctx, &error_string_utf8, error_string, &len)) {
69		return false;
70	}
71
72	*error_blob = data_blob_talloc(mem_ctx, NULL, 2 + len + 1);
73	if (!error_blob->data) {
74		return false;
75	}
76	RSSVAL(error_blob->data, 0, result_code);
77	memcpy(error_blob->data + 2, error_string_utf8, len + 1);
78	return true;
79}
80
81/* Return true if there is a valid error packet formed in the error_blob */
82static bool kpasswdd_make_unauth_error_reply(struct kdc_server *kdc,
83					    TALLOC_CTX *mem_ctx,
84					    uint16_t result_code,
85					    const char *error_string,
86					    DATA_BLOB *error_blob)
87{
88	bool ret;
89	int kret;
90	DATA_BLOB error_bytes;
91	krb5_data k5_error_bytes, k5_error_blob;
92	ret = kpasswdd_make_error_reply(kdc, mem_ctx, result_code, error_string,
93				       &error_bytes);
94	if (!ret) {
95		return false;
96	}
97	k5_error_bytes.data = error_bytes.data;
98	k5_error_bytes.length = error_bytes.length;
99	kret = krb5_mk_error(kdc->smb_krb5_context->krb5_context,
100			     result_code, NULL, &k5_error_bytes,
101			     NULL, NULL, NULL, NULL, &k5_error_blob);
102	if (kret) {
103		return false;
104	}
105	*error_blob = data_blob_talloc(mem_ctx, k5_error_blob.data, k5_error_blob.length);
106	krb5_data_free(&k5_error_blob);
107	if (!error_blob->data) {
108		return false;
109	}
110	return true;
111}
112
113static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc,
114					TALLOC_CTX *mem_ctx,
115					NTSTATUS status,
116					enum samr_RejectReason reject_reason,
117					struct samr_DomInfo1 *dominfo,
118					DATA_BLOB *error_blob)
119{
120	if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
121		return kpasswdd_make_error_reply(kdc, mem_ctx,
122						KRB5_KPASSWD_ACCESSDENIED,
123						"No such user when changing password",
124						error_blob);
125	}
126	if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
127		return kpasswdd_make_error_reply(kdc, mem_ctx,
128						KRB5_KPASSWD_ACCESSDENIED,
129						"Not permitted to change password",
130						error_blob);
131	}
132	if (dominfo && NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
133		const char *reject_string;
134		switch (reject_reason) {
135		case SAMR_REJECT_TOO_SHORT:
136			reject_string = talloc_asprintf(mem_ctx, "Password too short, password must be at least %d characters long",
137							dominfo->min_password_length);
138			break;
139		case SAMR_REJECT_COMPLEXITY:
140			reject_string = "Password does not meet complexity requirements";
141			break;
142		case SAMR_REJECT_IN_HISTORY:
143			reject_string = "Password is already in password history";
144			break;
145		case SAMR_REJECT_OTHER:
146		default:
147			reject_string = talloc_asprintf(mem_ctx, "Password must be at least %d characters long, and cannot match any of your %d previous passwords",
148							dominfo->min_password_length, dominfo->password_history_length);
149			break;
150		}
151		return kpasswdd_make_error_reply(kdc, mem_ctx,
152						KRB5_KPASSWD_SOFTERROR,
153						reject_string,
154						error_blob);
155	}
156	if (!NT_STATUS_IS_OK(status)) {
157		return kpasswdd_make_error_reply(kdc, mem_ctx,
158						 KRB5_KPASSWD_HARDERROR,
159						 talloc_asprintf(mem_ctx, "failed to set password: %s", nt_errstr(status)),
160						 error_blob);
161
162	}
163	return kpasswdd_make_error_reply(kdc, mem_ctx, KRB5_KPASSWD_SUCCESS,
164					"Password changed",
165					error_blob);
166}
167
168/*
169   A user password change
170
171   Return true if there is a valid error packet (or sucess) formed in
172   the error_blob
173*/
174static bool kpasswdd_change_password(struct kdc_server *kdc,
175				     TALLOC_CTX *mem_ctx,
176				     struct auth_session_info *session_info,
177				     const DATA_BLOB *password,
178				     DATA_BLOB *reply)
179{
180	NTSTATUS status;
181	enum samr_RejectReason reject_reason;
182	struct samr_DomInfo1 *dominfo;
183	struct ldb_context *samdb;
184
185	samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, system_session(mem_ctx, kdc->task->lp_ctx));
186	if (!samdb) {
187		return kpasswdd_make_error_reply(kdc, mem_ctx,
188						KRB5_KPASSWD_HARDERROR,
189						"Failed to open samdb",
190						reply);
191	}
192
193	DEBUG(3, ("Changing password of %s\\%s (%s)\n",
194		  session_info->server_info->domain_name,
195		  session_info->server_info->account_name,
196		  dom_sid_string(mem_ctx, session_info->security_token->user_sid)));
197
198	/* User password change */
199	status = samdb_set_password_sid(samdb, mem_ctx,
200					session_info->security_token->user_sid,
201					password, NULL, NULL,
202					true, /* this is a user password change */
203					&reject_reason,
204					&dominfo);
205	return kpasswd_make_pwchange_reply(kdc, mem_ctx,
206					   status,
207					   reject_reason,
208					   dominfo,
209					   reply);
210
211}
212
213static bool kpasswd_process_request(struct kdc_server *kdc,
214				    TALLOC_CTX *mem_ctx,
215				    struct gensec_security *gensec_security,
216				    uint16_t version,
217				    DATA_BLOB *input,
218				    DATA_BLOB *reply)
219{
220	struct auth_session_info *session_info;
221	size_t pw_len;
222
223	if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security,
224						 &session_info))) {
225		return kpasswdd_make_error_reply(kdc, mem_ctx,
226						KRB5_KPASSWD_HARDERROR,
227						"gensec_session_info failed!",
228						reply);
229	}
230
231	switch (version) {
232	case KRB5_KPASSWD_VERS_CHANGEPW:
233	{
234		DATA_BLOB password;
235		if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
236					       CH_UTF8, CH_UTF16,
237					       (const char *)input->data,
238					       input->length,
239					       (void **)&password.data, &pw_len, false)) {
240			return false;
241		}
242		password.length = pw_len;
243
244		return kpasswdd_change_password(kdc, mem_ctx, session_info,
245						&password, reply);
246		break;
247	}
248	case KRB5_KPASSWD_VERS_SETPW:
249	{
250		NTSTATUS status;
251		enum samr_RejectReason reject_reason = SAMR_REJECT_OTHER;
252		struct samr_DomInfo1 *dominfo = NULL;
253		struct ldb_context *samdb;
254		struct ldb_message *msg;
255		krb5_context context = kdc->smb_krb5_context->krb5_context;
256
257		ChangePasswdDataMS chpw;
258		DATA_BLOB password;
259
260		krb5_principal principal;
261		char *set_password_on_princ;
262		struct ldb_dn *set_password_on_dn;
263
264		size_t len;
265		int ret;
266
267		msg = ldb_msg_new(mem_ctx);
268		if (!msg) {
269			return false;
270		}
271
272		ret = decode_ChangePasswdDataMS(input->data, input->length,
273						&chpw, &len);
274		if (ret) {
275			return kpasswdd_make_error_reply(kdc, mem_ctx,
276							KRB5_KPASSWD_MALFORMED,
277							"failed to decode password change structure",
278							reply);
279		}
280
281		if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
282					       CH_UTF8, CH_UTF16,
283					       (const char *)chpw.newpasswd.data,
284					       chpw.newpasswd.length,
285					       (void **)&password.data, &pw_len, false)) {
286			free_ChangePasswdDataMS(&chpw);
287			return false;
288		}
289
290		password.length = pw_len;
291
292		if ((chpw.targname && !chpw.targrealm)
293		    || (!chpw.targname && chpw.targrealm)) {
294			return kpasswdd_make_error_reply(kdc, mem_ctx,
295							KRB5_KPASSWD_MALFORMED,
296							"Realm and principal must be both present, or neither present",
297							reply);
298		}
299		if (chpw.targname && chpw.targrealm) {
300#ifdef SAMBA4_INTERNAL_HEIMDAL
301			if (_krb5_principalname2krb5_principal(kdc->smb_krb5_context->krb5_context,
302							       &principal, *chpw.targname,
303							       *chpw.targrealm) != 0) {
304				free_ChangePasswdDataMS(&chpw);
305				return kpasswdd_make_error_reply(kdc, mem_ctx,
306								KRB5_KPASSWD_MALFORMED,
307								"failed to extract principal to set",
308								reply);
309
310			}
311#else /* SAMBA4_INTERNAL_HEIMDAL */
312				return kpasswdd_make_error_reply(kdc, mem_ctx,
313								KRB5_KPASSWD_BAD_VERSION,
314								"Operation Not Implemented",
315								reply);
316#endif /* SAMBA4_INTERNAL_HEIMDAL */
317		} else {
318			free_ChangePasswdDataMS(&chpw);
319			return kpasswdd_change_password(kdc, mem_ctx, session_info,
320							&password, reply);
321		}
322		free_ChangePasswdDataMS(&chpw);
323
324		if (krb5_unparse_name(context, principal, &set_password_on_princ) != 0) {
325			krb5_free_principal(context, principal);
326			return kpasswdd_make_error_reply(kdc, mem_ctx,
327							KRB5_KPASSWD_MALFORMED,
328							"krb5_unparse_name failed!",
329							reply);
330		}
331
332		krb5_free_principal(context, principal);
333
334		samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info);
335		if (!samdb) {
336			return kpasswdd_make_error_reply(kdc, mem_ctx,
337							 KRB5_KPASSWD_HARDERROR,
338							 "Unable to open database!",
339							 reply);
340		}
341
342		DEBUG(3, ("%s\\%s (%s) is changing password of %s\n",
343			  session_info->server_info->domain_name,
344			  session_info->server_info->account_name,
345			  dom_sid_string(mem_ctx, session_info->security_token->user_sid),
346			  set_password_on_princ));
347		ret = ldb_transaction_start(samdb);
348		if (ret) {
349			status = NT_STATUS_TRANSACTION_ABORTED;
350			return kpasswd_make_pwchange_reply(kdc, mem_ctx,
351							   status,
352							   SAMR_REJECT_OTHER,
353							   NULL,
354							   reply);
355		}
356
357		status = crack_user_principal_name(samdb, mem_ctx,
358						   set_password_on_princ,
359						   &set_password_on_dn, NULL);
360		free(set_password_on_princ);
361		if (!NT_STATUS_IS_OK(status)) {
362			ldb_transaction_cancel(samdb);
363			return kpasswd_make_pwchange_reply(kdc, mem_ctx,
364							   status,
365							   SAMR_REJECT_OTHER,
366							   NULL,
367							   reply);
368		}
369
370		msg = ldb_msg_new(mem_ctx);
371		if (msg == NULL) {
372			ldb_transaction_cancel(samdb);
373			status = NT_STATUS_NO_MEMORY;
374		} else {
375			msg->dn = ldb_dn_copy(msg, set_password_on_dn);
376			if (!msg->dn) {
377				status = NT_STATUS_NO_MEMORY;
378			}
379		}
380
381		if (NT_STATUS_IS_OK(status)) {
382			/* Admin password set */
383			status = samdb_set_password(samdb, mem_ctx,
384						    set_password_on_dn, NULL,
385						    msg, &password, NULL, NULL,
386						    false, /* this is not a user password change */
387						    &reject_reason, &dominfo);
388		}
389
390		if (NT_STATUS_IS_OK(status)) {
391			/* modify the samdb record */
392			ret = samdb_replace(samdb, mem_ctx, msg);
393			if (ret != 0) {
394				DEBUG(2,("Failed to modify record to set password on %s: %s\n",
395					 ldb_dn_get_linearized(msg->dn),
396					 ldb_errstring(samdb)));
397				status = NT_STATUS_ACCESS_DENIED;
398			}
399		}
400		if (NT_STATUS_IS_OK(status)) {
401			ret = ldb_transaction_commit(samdb);
402			if (ret != 0) {
403				DEBUG(1,("Failed to commit transaction to set password on %s: %s\n",
404					 ldb_dn_get_linearized(msg->dn),
405					 ldb_errstring(samdb)));
406				status = NT_STATUS_TRANSACTION_ABORTED;
407			}
408		} else {
409			ldb_transaction_cancel(samdb);
410		}
411		return kpasswd_make_pwchange_reply(kdc, mem_ctx,
412						   status,
413						   reject_reason,
414						   dominfo,
415						   reply);
416	}
417	default:
418		return kpasswdd_make_error_reply(kdc, mem_ctx,
419						 KRB5_KPASSWD_BAD_VERSION,
420						 talloc_asprintf(mem_ctx,
421								 "Protocol version %u not supported",
422								 version),
423						 reply);
424	}
425	return true;
426}
427
428bool kpasswdd_process(struct kdc_server *kdc,
429		      TALLOC_CTX *mem_ctx,
430		      DATA_BLOB *input,
431		      DATA_BLOB *reply,
432		      struct socket_address *peer_addr,
433		      struct socket_address *my_addr,
434		      int datagram_reply)
435{
436	bool ret;
437	const uint16_t header_len = 6;
438	uint16_t len;
439	uint16_t ap_req_len;
440	uint16_t krb_priv_len;
441	uint16_t version;
442	NTSTATUS nt_status;
443	DATA_BLOB ap_req, krb_priv_req;
444	DATA_BLOB krb_priv_rep = data_blob(NULL, 0);
445	DATA_BLOB ap_rep = data_blob(NULL, 0);
446	DATA_BLOB kpasswd_req, kpasswd_rep;
447	struct cli_credentials *server_credentials;
448	struct gensec_security *gensec_security;
449	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
450
451	char *keytab_name;
452
453	if (!tmp_ctx) {
454		return false;
455	}
456
457	/* Be parinoid.  We need to ensure we don't just let the
458	 * caller lead us into a buffer overflow */
459	if (input->length <= header_len) {
460		talloc_free(tmp_ctx);
461		return false;
462	}
463
464	len = RSVAL(input->data, 0);
465	if (input->length != len) {
466		talloc_free(tmp_ctx);
467		return false;
468	}
469
470	/* There are two different versions of this protocol so far,
471	 * plus others in the standards pipe.  Fortunetly they all
472	 * take a very similar framing */
473	version = RSVAL(input->data, 2);
474	ap_req_len = RSVAL(input->data, 4);
475	if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) {
476		talloc_free(tmp_ctx);
477		return false;
478	}
479
480	krb_priv_len = len - ap_req_len;
481	ap_req = data_blob_const(&input->data[header_len], ap_req_len);
482	krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len);
483
484	server_credentials = cli_credentials_init(tmp_ctx);
485	if (!server_credentials) {
486		DEBUG(1, ("Failed to init server credentials\n"));
487		return false;
488	}
489
490	/* We want the credentials subsystem to use the krb5 context
491	 * we already have, rather than a new context */
492	cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context);
493	cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
494
495	keytab_name = talloc_asprintf(server_credentials, "HDB:samba4&%p", kdc->hdb_samba4_context);
496
497	cli_credentials_set_username(server_credentials, "kadmin/changepw", CRED_SPECIFIED);
498	ret = cli_credentials_set_keytab_name(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, keytab_name, CRED_SPECIFIED);
499	if (ret != 0) {
500		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
501						       KRB5_KPASSWD_HARDERROR,
502						       talloc_asprintf(mem_ctx,
503								       "Failed to obtain server credentials for kadmin/changepw: %s\n",
504								       nt_errstr(nt_status)),
505						       &krb_priv_rep);
506		ap_rep.length = 0;
507		if (ret) {
508			goto reply;
509		}
510		talloc_free(tmp_ctx);
511		return ret;
512	}
513
514	/* We don't strictly need to call this wrapper, and could call
515	 * gensec_server_start directly, as we have no need for NTLM
516	 * and we have a PAC, but this ensures that the wrapper can be
517	 * safely extended for other helpful things in future */
518	nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx,
519					      kdc->task->msg_ctx,
520					      kdc->task->lp_ctx,
521					      server_credentials,
522					      "kpasswd",
523					      &gensec_security);
524	if (!NT_STATUS_IS_OK(nt_status)) {
525		talloc_free(tmp_ctx);
526		return false;
527	}
528
529	/* The kerberos PRIV packets include these addresses.  MIT
530	 * clients check that they are present */
531#if 0
532	/* Skip this part for now, it breaks with a NetAPP filer and
533	 * in any case where the client address is behind NAT.  If
534	 * older MIT clients need this, we might have to insert more
535	 * complex code */
536
537	nt_status = gensec_set_peer_addr(gensec_security, peer_addr);
538	if (!NT_STATUS_IS_OK(nt_status)) {
539		talloc_free(tmp_ctx);
540		return false;
541	}
542#endif
543
544	nt_status = gensec_set_my_addr(gensec_security, my_addr);
545	if (!NT_STATUS_IS_OK(nt_status)) {
546		talloc_free(tmp_ctx);
547		return false;
548	}
549
550	/* We want the GENSEC wrap calls to generate PRIV tokens */
551	gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
552
553	nt_status = gensec_start_mech_by_name(gensec_security, "krb5");
554	if (!NT_STATUS_IS_OK(nt_status)) {
555		talloc_free(tmp_ctx);
556		return false;
557	}
558
559	/* Accept the AP-REQ and generate teh AP-REP we need for the reply */
560	nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep);
561	if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
562
563		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
564						       KRB5_KPASSWD_HARDERROR,
565						       talloc_asprintf(mem_ctx,
566								       "gensec_update failed: %s",
567								       nt_errstr(nt_status)),
568						       &krb_priv_rep);
569		ap_rep.length = 0;
570		if (ret) {
571			goto reply;
572		}
573		talloc_free(tmp_ctx);
574		return ret;
575	}
576
577	/* Extract the data from the KRB-PRIV half of the message */
578	nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req);
579	if (!NT_STATUS_IS_OK(nt_status)) {
580		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
581						       KRB5_KPASSWD_HARDERROR,
582						       talloc_asprintf(mem_ctx,
583								       "gensec_unwrap failed: %s",
584								       nt_errstr(nt_status)),
585						       &krb_priv_rep);
586		ap_rep.length = 0;
587		if (ret) {
588			goto reply;
589		}
590		talloc_free(tmp_ctx);
591		return ret;
592	}
593
594	/* Figure out something to do with it (probably changing a password...) */
595	ret = kpasswd_process_request(kdc, tmp_ctx,
596				      gensec_security,
597				      version,
598				      &kpasswd_req, &kpasswd_rep);
599	if (!ret) {
600		/* Argh! */
601		return false;
602	}
603
604	/* And wrap up the reply: This ensures that the error message
605	 * or success can be verified by the client */
606	nt_status = gensec_wrap(gensec_security, tmp_ctx,
607				&kpasswd_rep, &krb_priv_rep);
608	if (!NT_STATUS_IS_OK(nt_status)) {
609		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
610						       KRB5_KPASSWD_HARDERROR,
611						       talloc_asprintf(mem_ctx,
612								       "gensec_wrap failed: %s",
613								       nt_errstr(nt_status)),
614						       &krb_priv_rep);
615		ap_rep.length = 0;
616		if (ret) {
617			goto reply;
618		}
619		talloc_free(tmp_ctx);
620		return ret;
621	}
622
623reply:
624	*reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len);
625	if (!reply->data) {
626		return false;
627	}
628
629	RSSVAL(reply->data, 0, reply->length);
630	RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */
631	RSSVAL(reply->data, 4, ap_rep.length);
632	memcpy(reply->data + header_len,
633	       ap_rep.data,
634	       ap_rep.length);
635	memcpy(reply->data + header_len + ap_rep.length,
636	       krb_priv_rep.data,
637	       krb_priv_rep.length);
638
639	talloc_free(tmp_ctx);
640	return ret;
641}
642
643