krb5_wrap: Add a talloc_ctx to smb_krb5_principal_get_realm()
[samba.git] / source4 / kdc / kpasswd-service-mit.c
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 "smbd/service_task.h"
24 #include "param/param.h"
25 #include "auth/auth.h"
26 #include "auth/gensec/gensec.h"
27 #include "kdc/kdc-server.h"
28 #include "kdc/kpasswd_glue.h"
29 #include "kdc/kpasswd-service.h"
30 #include "kdc/kpasswd-helper.h"
31
32 #define RFC3244_VERSION 0xff80
33
34 krb5_error_code decode_krb5_setpw_req(const krb5_data *code,
35                                       krb5_data **password_out,
36                                       krb5_principal *target_out);
37
38 static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
39                                                TALLOC_CTX *mem_ctx,
40                                                struct auth_session_info *session_info,
41                                                DATA_BLOB *password,
42                                                DATA_BLOB *kpasswd_reply,
43                                                const char **error_string)
44 {
45         NTSTATUS status;
46         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
47         enum samPwdChangeReason reject_reason;
48         const char *reject_string = NULL;
49         struct samr_DomInfo1 *dominfo;
50         bool ok;
51
52         status = samdb_kpasswd_change_password(mem_ctx,
53                                                kdc->task->lp_ctx,
54                                                kdc->task->event_ctx,
55                                                kdc->samdb,
56                                                session_info,
57                                                password,
58                                                &reject_reason,
59                                                &dominfo,
60                                                &reject_string,
61                                                &result);
62         if (!NT_STATUS_IS_OK(status)) {
63                 ok = kpasswd_make_error_reply(mem_ctx,
64                                               KRB5_KPASSWD_ACCESSDENIED,
65                                               reject_string,
66                                               kpasswd_reply);
67                 if (!ok) {
68                         *error_string = "Failed to create reply";
69                         return KRB5_KPASSWD_HARDERROR;
70                 }
71                 /* We want to send an an authenticated packet. */
72                 return 0;
73         }
74
75         ok = kpasswd_make_pwchange_reply(mem_ctx,
76                                          result,
77                                          reject_reason,
78                                          dominfo,
79                                          kpasswd_reply);
80         if (!ok) {
81                 *error_string = "Failed to create reply";
82                 return KRB5_KPASSWD_HARDERROR;
83         }
84
85         return 0;
86 }
87
88 static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
89                                             TALLOC_CTX *mem_ctx,
90                                             struct auth_session_info *session_info,
91                                             DATA_BLOB *decoded_data,
92                                             DATA_BLOB *kpasswd_reply,
93                                             const char **error_string)
94 {
95         krb5_context context = kdc->smb_krb5_context->krb5_context;
96         krb5_data k_dec_data;
97         krb5_data *k_clear_data;
98         krb5_principal target_principal;
99         krb5_error_code code;
100         DATA_BLOB password;
101         char *target_realm = NULL;
102         char *target_name = NULL;
103         char *target_principal_string = NULL;
104         bool is_service_principal = false;
105         bool ok;
106         size_t num_components;
107         enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR;
108         struct samr_DomInfo1 *dominfo = NULL;
109         NTSTATUS status;
110
111         k_dec_data.length = decoded_data->length;
112         k_dec_data.data   = (char *)decoded_data->data;
113
114         code = decode_krb5_setpw_req(&k_dec_data,
115                                      &k_clear_data,
116                                      &target_principal);
117         if (code != 0) {
118                 DBG_WARNING("decode_krb5_setpw_req failed: %s\n",
119                             error_message(code));
120                 ok = kpasswd_make_error_reply(mem_ctx,
121                                               KRB5_KPASSWD_MALFORMED,
122                                               "Failed to decode packet",
123                                               kpasswd_reply);
124                 if (!ok) {
125                         *error_string = "Failed to create reply";
126                         return KRB5_KPASSWD_HARDERROR;
127                 }
128                 return 0;
129         }
130
131         ok = convert_string_talloc_handle(mem_ctx,
132                                           lpcfg_iconv_handle(kdc->task->lp_ctx),
133                                           CH_UTF8,
134                                           CH_UTF16,
135                                           (const char *)k_clear_data->data,
136                                           k_clear_data->length,
137                                           (void **)&password.data,
138                                           &password.length);
139         krb5_free_data(context, k_clear_data);
140         if (!ok) {
141                 DBG_WARNING("String conversion failed\n");
142                 *error_string = "String conversion failed";
143                 return KRB5_KPASSWD_HARDERROR;
144         }
145
146         target_realm = smb_krb5_principal_get_realm(
147                 mem_ctx, context, target_principal);
148         code = krb5_unparse_name_flags(context,
149                                        target_principal,
150                                        KRB5_PRINCIPAL_UNPARSE_NO_REALM,
151                                        &target_name);
152         if (code != 0) {
153                 DBG_WARNING("Failed to parse principal\n");
154                 *error_string = "String conversion failed";
155                 return KRB5_KPASSWD_HARDERROR;
156         }
157
158         if ((target_name != NULL && target_realm == NULL) ||
159             (target_name == NULL && target_realm != NULL)) {
160                 krb5_free_principal(context, target_principal);
161                 TALLOC_FREE(target_realm);
162                 SAFE_FREE(target_name);
163
164                 ok = kpasswd_make_error_reply(mem_ctx,
165                                               KRB5_KPASSWD_MALFORMED,
166                                               "Realm and principal must be "
167                                               "both present, or neither "
168                                               "present",
169                                               kpasswd_reply);
170                 if (!ok) {
171                         *error_string = "Failed to create reply";
172                         return KRB5_KPASSWD_HARDERROR;
173                 }
174                 return 0;
175         }
176
177         if (target_name != NULL && target_realm != NULL) {
178                 TALLOC_FREE(target_realm);
179                 SAFE_FREE(target_name);
180         } else {
181                 krb5_free_principal(context, target_principal);
182                 TALLOC_FREE(target_realm);
183                 SAFE_FREE(target_name);
184
185                 return kpasswd_change_password(kdc,
186                                                mem_ctx,
187                                                session_info,
188                                                &password,
189                                                kpasswd_reply,
190                                                error_string);
191         }
192
193         num_components = krb5_princ_size(context, target_principal);
194         if (num_components >= 2) {
195                 is_service_principal = true;
196                 code = krb5_unparse_name_flags(context,
197                                                target_principal,
198                                                KRB5_PRINCIPAL_UNPARSE_SHORT,
199                                                &target_principal_string);
200         } else {
201                 code = krb5_unparse_name(context,
202                                          target_principal,
203                                          &target_principal_string);
204         }
205         krb5_free_principal(context, target_principal);
206         if (code != 0) {
207                 ok = kpasswd_make_error_reply(mem_ctx,
208                                               KRB5_KPASSWD_MALFORMED,
209                                               "Failed to parse principal",
210                                               kpasswd_reply);
211                 if (!ok) {
212                         *error_string = "Failed to create reply";
213                         return KRB5_KPASSWD_HARDERROR;
214                 }
215         }
216
217         status = kpasswd_samdb_set_password(mem_ctx,
218                                             kdc->task->event_ctx,
219                                             kdc->task->lp_ctx,
220                                             session_info,
221                                             is_service_principal,
222                                             target_principal_string,
223                                             &password,
224                                             &reject_reason,
225                                             &dominfo);
226         if (!NT_STATUS_IS_OK(status)) {
227                 DBG_ERR("kpasswd_samdb_set_password failed - %s\n",
228                         nt_errstr(status));
229         }
230
231         ok = kpasswd_make_pwchange_reply(mem_ctx,
232                                          status,
233                                          reject_reason,
234                                          dominfo,
235                                          kpasswd_reply);
236         if (!ok) {
237                 *error_string = "Failed to create reply";
238                 return KRB5_KPASSWD_HARDERROR;
239         }
240
241         return 0;
242 }
243
244 krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
245                                        TALLOC_CTX *mem_ctx,
246                                        struct gensec_security *gensec_security,
247                                        uint16_t verno,
248                                        DATA_BLOB *decoded_data,
249                                        DATA_BLOB *kpasswd_reply,
250                                        const char **error_string)
251 {
252         struct auth_session_info *session_info;
253         NTSTATUS status;
254
255         status = gensec_session_info(gensec_security,
256                                      mem_ctx,
257                                      &session_info);
258         if (!NT_STATUS_IS_OK(status)) {
259                 *error_string = talloc_asprintf(mem_ctx,
260                                                 "gensec_session_info failed - "
261                                                 "%s",
262                                                 nt_errstr(status));
263                 return KRB5_KPASSWD_HARDERROR;
264         }
265
266         switch(verno) {
267         case 1: {
268                 DATA_BLOB password;
269                 bool ok;
270
271                 ok = convert_string_talloc_handle(mem_ctx,
272                                                   lpcfg_iconv_handle(kdc->task->lp_ctx),
273                                                   CH_UTF8,
274                                                   CH_UTF16,
275                                                   (const char *)decoded_data->data,
276                                                   decoded_data->length,
277                                                   (void **)&password.data,
278                                                   &password.length);
279                 if (!ok) {
280                         *error_string = "String conversion failed!";
281                         DBG_WARNING("%s\n", *error_string);
282                         return KRB5_KPASSWD_HARDERROR;
283                 }
284
285                 return kpasswd_change_password(kdc,
286                                                mem_ctx,
287                                                session_info,
288                                                &password,
289                                                kpasswd_reply,
290                                                error_string);
291         }
292         case RFC3244_VERSION: {
293                 return kpasswd_set_password(kdc,
294                                             mem_ctx,
295                                             session_info,
296                                             decoded_data,
297                                             kpasswd_reply,
298                                             error_string);
299         }
300         default:
301                 return KRB5_KPASSWD_BAD_VERSION;
302         }
303
304         return 0;
305 }