Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Samba kpasswd implementation
5 :
6 : Copyright (c) 2005 Andrew Bartlett <abartlet@samba.org>
7 : Copyright (c) 2016 Andreas Schneider <asn@samba.org>
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 "system/kerberos.h"
25 : #include "librpc/gen_ndr/samr.h"
26 : #include "dsdb/samdb/samdb.h"
27 : #include "auth/auth.h"
28 : #include "kdc/kpasswd-helper.h"
29 :
30 : #undef DBGC_CLASS
31 : #define DBGC_CLASS DBGC_KERBEROS
32 :
33 118 : bool kpasswd_make_error_reply(TALLOC_CTX *mem_ctx,
34 : krb5_error_code error_code,
35 : const char *error_string,
36 : DATA_BLOB *error_data)
37 : {
38 0 : bool ok;
39 0 : char *s;
40 0 : size_t slen;
41 :
42 118 : if (error_code == 0) {
43 55 : DBG_DEBUG("kpasswd reply - %s\n", error_string);
44 : } else {
45 63 : DBG_INFO("kpasswd reply - %s\n", error_string);
46 : }
47 :
48 118 : ok = push_utf8_talloc(mem_ctx, &s, error_string, &slen);
49 118 : if (!ok) {
50 0 : return false;
51 : }
52 :
53 : /*
54 : * The string 's' has one terminating nul-byte which is also
55 : * reflected by 'slen'. We subtract it from the length.
56 : */
57 118 : if (slen < 1) {
58 0 : talloc_free(s);
59 0 : return false;
60 : }
61 118 : slen--;
62 :
63 : /* Two bytes are added to the length to account for the error code. */
64 118 : if (2 + slen < slen) {
65 0 : talloc_free(s);
66 0 : return false;
67 : }
68 118 : error_data->length = 2 + slen;
69 118 : error_data->data = talloc_size(mem_ctx, error_data->length);
70 118 : if (error_data->data == NULL) {
71 0 : talloc_free(s);
72 0 : return false;
73 : }
74 :
75 118 : RSSVAL(error_data->data, 0, error_code);
76 118 : memcpy(error_data->data + 2, s, slen);
77 :
78 118 : talloc_free(s);
79 :
80 118 : return true;
81 : }
82 :
83 80 : bool kpasswd_make_pwchange_reply(TALLOC_CTX *mem_ctx,
84 : NTSTATUS status,
85 : enum samPwdChangeReason reject_reason,
86 : struct samr_DomInfo1 *dominfo,
87 : DATA_BLOB *error_blob)
88 : {
89 80 : const char *reject_string = NULL;
90 :
91 80 : if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
92 15 : return kpasswd_make_error_reply(mem_ctx,
93 : KRB5_KPASSWD_ACCESSDENIED,
94 : "No such user when changing password",
95 : error_blob);
96 80 : } else if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
97 15 : return kpasswd_make_error_reply(mem_ctx,
98 : KRB5_KPASSWD_ACCESSDENIED,
99 : "Not permitted to change password",
100 : error_blob);
101 : }
102 65 : if (dominfo != NULL &&
103 65 : NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
104 10 : switch (reject_reason) {
105 4 : case SAM_PWD_CHANGE_PASSWORD_TOO_SHORT:
106 0 : reject_string =
107 4 : talloc_asprintf(mem_ctx,
108 : "Password too short, password "
109 : "must be at least %d characters "
110 : "long.",
111 4 : dominfo->min_password_length);
112 4 : if (reject_string == NULL) {
113 0 : reject_string = "Password too short";
114 : }
115 4 : break;
116 6 : case SAM_PWD_CHANGE_NOT_COMPLEX:
117 6 : reject_string = "Password does not meet complexity "
118 : "requirements";
119 6 : break;
120 0 : case SAM_PWD_CHANGE_PWD_IN_HISTORY:
121 0 : reject_string =
122 0 : talloc_asprintf(mem_ctx,
123 : "Password is already in password "
124 : "history. New password must not "
125 : "match any of your %d previous "
126 : "passwords.",
127 0 : dominfo->password_history_length);
128 0 : if (reject_string == NULL) {
129 0 : reject_string = "Password is already in password "
130 : "history";
131 : }
132 0 : break;
133 0 : default:
134 0 : reject_string = "Password change rejected, password "
135 : "changes may not be permitted on this "
136 : "account, or the minimum password age "
137 : "may not have elapsed.";
138 0 : break;
139 : }
140 :
141 10 : return kpasswd_make_error_reply(mem_ctx,
142 : KRB5_KPASSWD_SOFTERROR,
143 : reject_string,
144 : error_blob);
145 : }
146 :
147 55 : if (!NT_STATUS_IS_OK(status)) {
148 0 : reject_string = talloc_asprintf(mem_ctx,
149 : "Failed to set password: %s",
150 : nt_errstr(status));
151 0 : if (reject_string == NULL) {
152 0 : reject_string = "Failed to set password";
153 : }
154 0 : return kpasswd_make_error_reply(mem_ctx,
155 : KRB5_KPASSWD_HARDERROR,
156 : reject_string,
157 : error_blob);
158 : }
159 :
160 55 : return kpasswd_make_error_reply(mem_ctx,
161 : KRB5_KPASSWD_SUCCESS,
162 : "Password changed",
163 : error_blob);
164 : }
165 :
166 23 : NTSTATUS kpasswd_samdb_set_password(TALLOC_CTX *mem_ctx,
167 : struct tevent_context *event_ctx,
168 : struct loadparm_context *lp_ctx,
169 : struct auth_session_info *session_info,
170 : bool is_service_principal,
171 : const char *target_principal_name,
172 : DATA_BLOB *password,
173 : enum samPwdChangeReason *reject_reason,
174 : struct samr_DomInfo1 **dominfo)
175 : {
176 0 : NTSTATUS status;
177 0 : struct ldb_context *samdb;
178 23 : struct ldb_dn *target_dn = NULL;
179 0 : int rc;
180 :
181 23 : samdb = samdb_connect(mem_ctx,
182 : event_ctx,
183 : lp_ctx,
184 : session_info,
185 : NULL,
186 : 0);
187 23 : if (samdb == NULL) {
188 0 : return NT_STATUS_INTERNAL_DB_CORRUPTION;
189 : }
190 :
191 23 : DBG_INFO("%s\\%s (%s) is changing password of %s\n",
192 : session_info->info->domain_name,
193 : session_info->info->account_name,
194 : dom_sid_string(mem_ctx,
195 : &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]),
196 : target_principal_name);
197 :
198 23 : rc = ldb_transaction_start(samdb);
199 23 : if (rc != LDB_SUCCESS) {
200 0 : return NT_STATUS_TRANSACTION_ABORTED;
201 : }
202 :
203 23 : if (is_service_principal) {
204 0 : status = crack_service_principal_name(samdb,
205 : mem_ctx,
206 : target_principal_name,
207 : &target_dn,
208 : NULL);
209 : } else {
210 23 : status = crack_user_principal_name(samdb,
211 : mem_ctx,
212 : target_principal_name,
213 : &target_dn,
214 : NULL);
215 : }
216 23 : if (!NT_STATUS_IS_OK(status)) {
217 0 : ldb_transaction_cancel(samdb);
218 0 : return status;
219 : }
220 :
221 23 : status = samdb_set_password(samdb,
222 : mem_ctx,
223 : target_dn,
224 : password,
225 : NULL, /* ntNewHash */
226 : DSDB_PASSWORD_RESET,
227 : reject_reason,
228 : dominfo);
229 23 : if (NT_STATUS_IS_OK(status)) {
230 8 : rc = ldb_transaction_commit(samdb);
231 8 : if (rc != LDB_SUCCESS) {
232 0 : DBG_WARNING("Failed to commit transaction to "
233 : "set password on %s: %s\n",
234 : ldb_dn_get_linearized(target_dn),
235 : ldb_errstring(samdb));
236 0 : return NT_STATUS_TRANSACTION_ABORTED;
237 : }
238 : } else {
239 15 : ldb_transaction_cancel(samdb);
240 : }
241 :
242 23 : return status;
243 : }
244 :
245 98 : krb5_error_code kpasswd_check_non_tgt(struct auth_session_info *session_info,
246 : const char **error_string)
247 : {
248 98 : switch(session_info->ticket_type) {
249 8 : case TICKET_TYPE_TGT:
250 : /* TGTs are disallowed here. */
251 8 : *error_string = "A TGT may not be used as a ticket to kpasswd";
252 8 : return KRB5_KPASSWD_AUTHERROR;
253 90 : case TICKET_TYPE_NON_TGT:
254 : /* Non-TGTs are permitted, and expected. */
255 90 : break;
256 0 : default:
257 : /* In case we forgot to set the type. */
258 0 : *error_string = "Failed to ascertain that ticket to kpasswd is not a TGT";
259 0 : return KRB5_KPASSWD_HARDERROR;
260 : }
261 :
262 90 : return 0;
263 : }
|