s4:kdc: Rename ‘user_info_dc’ to ‘client_info’
[samba.git] / source4 / kdc / kpasswd-helper.c
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 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         bool ok;
39         char *s;
40         size_t slen;
41
42         if (error_code == 0) {
43                 DBG_DEBUG("kpasswd reply - %s\n", error_string);
44         } else {
45                 DBG_INFO("kpasswd reply - %s\n", error_string);
46         }
47
48         ok = push_utf8_talloc(mem_ctx, &s, error_string, &slen);
49         if (!ok) {
50                 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         if (slen < 1) {
58                 talloc_free(s);
59                 return false;
60         }
61         slen--;
62
63         /* Two bytes are added to the length to account for the error code. */
64         if (2 + slen < slen) {
65                 talloc_free(s);
66                 return false;
67         }
68         error_data->length = 2 + slen;
69         error_data->data = talloc_size(mem_ctx, error_data->length);
70         if (error_data->data == NULL) {
71                 talloc_free(s);
72                 return false;
73         }
74
75         RSSVAL(error_data->data, 0, error_code);
76         memcpy(error_data->data + 2, s, slen);
77
78         talloc_free(s);
79
80         return true;
81 }
82
83 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         const char *reject_string = NULL;
90
91         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
92                 return kpasswd_make_error_reply(mem_ctx,
93                                                 KRB5_KPASSWD_ACCESSDENIED,
94                                                 "No such user when changing password",
95                                                 error_blob);
96         } else if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
97                 return kpasswd_make_error_reply(mem_ctx,
98                                                 KRB5_KPASSWD_ACCESSDENIED,
99                                                 "Not permitted to change password",
100                                                 error_blob);
101         }
102         if (dominfo != NULL &&
103             NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
104                 switch (reject_reason) {
105                 case SAM_PWD_CHANGE_PASSWORD_TOO_SHORT:
106                         reject_string =
107                                 talloc_asprintf(mem_ctx,
108                                                 "Password too short, password "
109                                                 "must be at least %d characters "
110                                                 "long.",
111                                                 dominfo->min_password_length);
112                         if (reject_string == NULL) {
113                                 reject_string = "Password too short";
114                         }
115                         break;
116                 case SAM_PWD_CHANGE_NOT_COMPLEX:
117                         reject_string = "Password does not meet complexity "
118                                         "requirements";
119                         break;
120                 case SAM_PWD_CHANGE_PWD_IN_HISTORY:
121                         reject_string =
122                                 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                                                 dominfo->password_history_length);
128                         if (reject_string == NULL) {
129                                 reject_string = "Password is already in password "
130                                                 "history";
131                         }
132                         break;
133                 default:
134                         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                         break;
139                 }
140
141                 return kpasswd_make_error_reply(mem_ctx,
142                                                 KRB5_KPASSWD_SOFTERROR,
143                                                 reject_string,
144                                                 error_blob);
145         }
146
147         if (!NT_STATUS_IS_OK(status)) {
148                 reject_string = talloc_asprintf(mem_ctx,
149                                                 "Failed to set password: %s",
150                                                 nt_errstr(status));
151                 if (reject_string == NULL) {
152                         reject_string = "Failed to set password";
153                 }
154                 return kpasswd_make_error_reply(mem_ctx,
155                                                 KRB5_KPASSWD_HARDERROR,
156                                                 reject_string,
157                                                 error_blob);
158         }
159
160         return kpasswd_make_error_reply(mem_ctx,
161                                         KRB5_KPASSWD_SUCCESS,
162                                         "Password changed",
163                                         error_blob);
164 }
165
166 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         NTSTATUS status;
177         struct ldb_context *samdb;
178         struct ldb_dn *target_dn = NULL;
179         int rc;
180
181         samdb = samdb_connect(mem_ctx,
182                               event_ctx,
183                               lp_ctx,
184                               session_info,
185                               NULL,
186                               0);
187         if (samdb == NULL) {
188                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
189         }
190
191         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         rc = ldb_transaction_start(samdb);
199         if (rc != LDB_SUCCESS) {
200                 return NT_STATUS_TRANSACTION_ABORTED;
201         }
202
203         if (is_service_principal) {
204                 status = crack_service_principal_name(samdb,
205                                                       mem_ctx,
206                                                       target_principal_name,
207                                                       &target_dn,
208                                                       NULL);
209         } else {
210                 status = crack_user_principal_name(samdb,
211                                                    mem_ctx,
212                                                    target_principal_name,
213                                                    &target_dn,
214                                                    NULL);
215         }
216         if (!NT_STATUS_IS_OK(status)) {
217                 ldb_transaction_cancel(samdb);
218                 return status;
219         }
220
221         status = samdb_set_password(samdb,
222                                     mem_ctx,
223                                     target_dn,
224                                     NULL, /* domain_dn */
225                                     password,
226                                     NULL, /* ntNewHash */
227                                     DSDB_PASSWORD_RESET,
228                                     reject_reason,
229                                     dominfo);
230         if (NT_STATUS_IS_OK(status)) {
231                 rc = ldb_transaction_commit(samdb);
232                 if (rc != LDB_SUCCESS) {
233                         DBG_WARNING("Failed to commit transaction to "
234                                     "set password on %s: %s\n",
235                                     ldb_dn_get_linearized(target_dn),
236                                     ldb_errstring(samdb));
237                         return NT_STATUS_TRANSACTION_ABORTED;
238                 }
239         } else {
240                 ldb_transaction_cancel(samdb);
241         }
242
243         return status;
244 }
245
246 krb5_error_code kpasswd_check_non_tgt(struct auth_session_info *session_info,
247                                       const char **error_string)
248 {
249         switch(session_info->ticket_type) {
250         case TICKET_TYPE_TGT:
251                 /* TGTs are disallowed here. */
252                 *error_string = "A TGT may not be used as a ticket to kpasswd";
253                 return KRB5_KPASSWD_AUTHERROR;
254         case TICKET_TYPE_NON_TGT:
255                 /* Non-TGTs are permitted, and expected. */
256                 break;
257         default:
258                 /* In case we forgot to set the type. */
259                 *error_string = "Failed to ascertain that ticket to kpasswd is not a TGT";
260                 return KRB5_KPASSWD_HARDERROR;
261         }
262
263         return 0;
264 }