~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/kdc/kpasswdd.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 */
 
47
struct 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 */
 
57
static 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 */
 
82
static 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
 
 
113
static 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
*/
 
174
static 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
 
 
213
static 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
 
 
428
bool 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
        if (!tmp_ctx) {
 
452
                return false;
 
453
        }
 
454
 
 
455
        /* Be parinoid.  We need to ensure we don't just let the
 
456
         * caller lead us into a buffer overflow */
 
457
        if (input->length <= header_len) {
 
458
                talloc_free(tmp_ctx);
 
459
                return false;
 
460
        }
 
461
 
 
462
        len = RSVAL(input->data, 0);
 
463
        if (input->length != len) {
 
464
                talloc_free(tmp_ctx);
 
465
                return false;
 
466
        }
 
467
 
 
468
        /* There are two different versions of this protocol so far,
 
469
         * plus others in the standards pipe.  Fortunetly they all
 
470
         * take a very similar framing */
 
471
        version = RSVAL(input->data, 2);
 
472
        ap_req_len = RSVAL(input->data, 4);
 
473
        if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) {
 
474
                talloc_free(tmp_ctx);
 
475
                return false;
 
476
        }
 
477
        
 
478
        krb_priv_len = len - ap_req_len;
 
479
        ap_req = data_blob_const(&input->data[header_len], ap_req_len);
 
480
        krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len);
 
481
        
 
482
        server_credentials = cli_credentials_init(tmp_ctx);
 
483
        if (!server_credentials) {
 
484
                DEBUG(1, ("Failed to init server credentials\n"));
 
485
                return false;
 
486
        }
 
487
 
 
488
        /* We want the credentials subsystem to use the krb5 context
 
489
         * we already have, rather than a new context */        
 
490
        cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context);
 
491
        cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
 
492
        nt_status = cli_credentials_set_stored_principal(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, "kadmin/changepw");
 
493
        if (!NT_STATUS_IS_OK(nt_status)) {
 
494
                ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, 
 
495
                                                       KRB5_KPASSWD_HARDERROR,
 
496
                                                       talloc_asprintf(mem_ctx, 
 
497
                                                                       "Failed to obtain server credentials for kadmin/changepw: %s\n", 
 
498
                                                                       nt_errstr(nt_status)),
 
499
                                                       &krb_priv_rep);
 
500
                ap_rep.length = 0;
 
501
                if (ret) {
 
502
                        goto reply;
 
503
                }
 
504
                talloc_free(tmp_ctx);
 
505
                return ret;
 
506
        }
 
507
        
 
508
        /* We don't strictly need to call this wrapper, and could call
 
509
         * gensec_server_start directly, as we have no need for NTLM
 
510
         * and we have a PAC, but this ensures that the wrapper can be
 
511
         * safely extended for other helpful things in future */
 
512
        nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx, 
 
513
                                              kdc->task->msg_ctx,
 
514
                                              kdc->task->lp_ctx,
 
515
                                              server_credentials,
 
516
                                              "kpasswd", 
 
517
                                              &gensec_security);
 
518
        if (!NT_STATUS_IS_OK(nt_status)) {
 
519
                talloc_free(tmp_ctx);
 
520
                return false;
 
521
        }
 
522
 
 
523
        /* The kerberos PRIV packets include these addresses.  MIT
 
524
         * clients check that they are present */
 
525
        nt_status = gensec_set_peer_addr(gensec_security, peer_addr);
 
526
        if (!NT_STATUS_IS_OK(nt_status)) {
 
527
                talloc_free(tmp_ctx);
 
528
                return false;
 
529
        }
 
530
        nt_status = gensec_set_my_addr(gensec_security, my_addr);
 
531
        if (!NT_STATUS_IS_OK(nt_status)) {
 
532
                talloc_free(tmp_ctx);
 
533
                return false;
 
534
        }
 
535
 
 
536
        /* We want the GENSEC wrap calls to generate PRIV tokens */
 
537
        gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
 
538
 
 
539
        nt_status = gensec_start_mech_by_name(gensec_security, "krb5");
 
540
        if (!NT_STATUS_IS_OK(nt_status)) {
 
541
                talloc_free(tmp_ctx);
 
542
                return false;
 
543
        }
 
544
 
 
545
        /* Accept the AP-REQ and generate teh AP-REP we need for the reply */
 
546
        nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep);
 
547
        if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 
548
                
 
549
                ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, 
 
550
                                                       KRB5_KPASSWD_HARDERROR,
 
551
                                                       talloc_asprintf(mem_ctx, 
 
552
                                                                       "gensec_update failed: %s", 
 
553
                                                                       nt_errstr(nt_status)),
 
554
                                                       &krb_priv_rep);
 
555
                ap_rep.length = 0;
 
556
                if (ret) {
 
557
                        goto reply;
 
558
                }
 
559
                talloc_free(tmp_ctx);
 
560
                return ret;
 
561
        }
 
562
 
 
563
        /* Extract the data from the KRB-PRIV half of the message */
 
564
        nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req);
 
565
        if (!NT_STATUS_IS_OK(nt_status)) {
 
566
                ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, 
 
567
                                                       KRB5_KPASSWD_HARDERROR,
 
568
                                                       talloc_asprintf(mem_ctx, 
 
569
                                                                       "gensec_unwrap failed: %s", 
 
570
                                                                       nt_errstr(nt_status)),
 
571
                                                       &krb_priv_rep);
 
572
                ap_rep.length = 0;
 
573
                if (ret) {
 
574
                        goto reply;
 
575
                }
 
576
                talloc_free(tmp_ctx);
 
577
                return ret;
 
578
        }
 
579
 
 
580
        /* Figure out something to do with it (probably changing a password...) */
 
581
        ret = kpasswd_process_request(kdc, tmp_ctx, 
 
582
                                      gensec_security, 
 
583
                                      version, 
 
584
                                      &kpasswd_req, &kpasswd_rep); 
 
585
        if (!ret) {
 
586
                /* Argh! */
 
587
                return false;
 
588
        }
 
589
 
 
590
        /* And wrap up the reply: This ensures that the error message
 
591
         * or success can be verified by the client */
 
592
        nt_status = gensec_wrap(gensec_security, tmp_ctx, 
 
593
                                &kpasswd_rep, &krb_priv_rep);
 
594
        if (!NT_STATUS_IS_OK(nt_status)) {
 
595
                ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, 
 
596
                                                       KRB5_KPASSWD_HARDERROR,
 
597
                                                       talloc_asprintf(mem_ctx, 
 
598
                                                                       "gensec_wrap failed: %s", 
 
599
                                                                       nt_errstr(nt_status)),
 
600
                                                       &krb_priv_rep);
 
601
                ap_rep.length = 0;
 
602
                if (ret) {
 
603
                        goto reply;
 
604
                }
 
605
                talloc_free(tmp_ctx);
 
606
                return ret;
 
607
        }
 
608
        
 
609
reply:
 
610
        *reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len);
 
611
        if (!reply->data) {
 
612
                return false;
 
613
        }
 
614
 
 
615
        RSSVAL(reply->data, 0, reply->length);
 
616
        RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */
 
617
        RSSVAL(reply->data, 4, ap_rep.length);
 
618
        memcpy(reply->data + header_len, 
 
619
               ap_rep.data, 
 
620
               ap_rep.length);
 
621
        memcpy(reply->data + header_len + ap_rep.length, 
 
622
               krb_priv_rep.data, 
 
623
               krb_priv_rep.length);
 
624
 
 
625
        talloc_free(tmp_ctx);
 
626
        return ret;
 
627
}
 
628