~ubuntu-branches/ubuntu/maverick/krb5/maverick

« back to all changes in this revision

Viewing changes to src/kadmin/server/schpw.c

  • Committer: Bazaar Package Importer
  • Author(s): Sam Hartman
  • Date: 2009-05-07 16:16:34 UTC
  • mfrom: (13.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20090507161634-xqyk0s9na0le4flj
Tags: 1.7dfsg~beta1-4
When  decrypting the TGS response fails with the subkey, try with the
session key to work around Heimdal bug, Closes: #527353 

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
#define GETSOCKNAME_ARG3_TYPE int
12
12
#endif
13
13
 
 
14
#define RFC3244_VERSION 0xff80
 
15
 
14
16
krb5_error_code
15
 
process_chpw_request(context, server_handle, realm, s, keytab, sockin, 
16
 
                     req, rep)
 
17
process_chpw_request(context, server_handle, realm, keytab,
 
18
                     local_faddr, remote_faddr, req, rep)
17
19
     krb5_context context;
18
20
     void *server_handle;
19
21
     char *realm;
20
 
     int s;
21
22
     krb5_keytab keytab;
22
 
     struct sockaddr_in *sockin;
 
23
     krb5_fulladdr *local_faddr;
 
24
     krb5_fulladdr *remote_faddr;
23
25
     krb5_data *req;
24
26
     krb5_data *rep;
25
27
{
26
28
    krb5_error_code ret;
27
29
    char *ptr;
28
30
    int plen, vno;
29
 
    krb5_address local_kaddr, remote_kaddr;
30
 
    int allocated_mem = 0;  
31
31
    krb5_data ap_req, ap_rep;
32
32
    krb5_auth_context auth_context;
33
33
    krb5_principal changepw;
 
34
    krb5_principal client, target = NULL;
34
35
    krb5_ticket *ticket;
35
36
    krb5_data cipher, clear;
36
 
    struct sockaddr local_addr, remote_addr;
37
 
    GETSOCKNAME_ARG3_TYPE addrlen;
38
37
    krb5_replay_data replay;
39
38
    krb5_error krberror;
40
39
    int numresult;
41
40
    char strresult[1024];
42
 
    char *clientstr;
 
41
    char *clientstr = NULL, *targetstr = NULL;
 
42
    const char *errmsg = NULL;
43
43
    size_t clen;
44
44
    char *cdots;
 
45
    struct sockaddr_storage ss;
 
46
    socklen_t salen;
 
47
    char addrbuf[100];
 
48
    krb5_address *addr = remote_faddr->address;
45
49
 
46
50
    ret = 0;
47
51
    rep->length = 0;
58
62
           or the caller passed in garbage */
59
63
        ret = KRB5KRB_AP_ERR_MODIFIED;
60
64
        numresult = KRB5_KPASSWD_MALFORMED;
61
 
        strcpy(strresult, "Request was truncated");
 
65
        strlcpy(strresult, "Request was truncated", sizeof(strresult));
62
66
        goto chpwfail;
63
67
    }
64
68
 
77
81
    vno = (*ptr++ & 0xff) ;
78
82
    vno = (vno<<8) | (*ptr++ & 0xff);
79
83
 
80
 
    if (vno != 1) {
 
84
    if (vno != 1 && vno != RFC3244_VERSION) {
81
85
        ret = KRB5KDC_ERR_BAD_PVNO;
82
86
        numresult = KRB5_KPASSWD_BAD_VERSION;
83
 
        sprintf(strresult,
84
 
                "Request contained unknown protocol version number %d", vno);
 
87
        snprintf(strresult, sizeof(strresult),
 
88
                 "Request contained unknown protocol version number %d", vno);
85
89
        goto chpwfail;
86
90
    }
87
91
 
93
97
    if (ptr + ap_req.length >= req->data + req->length) {
94
98
        ret = KRB5KRB_AP_ERR_MODIFIED;
95
99
        numresult = KRB5_KPASSWD_MALFORMED;
96
 
        strcpy(strresult, "Request was truncated in AP-REQ");
 
100
        strlcpy(strresult, "Request was truncated in AP-REQ",
 
101
                sizeof(strresult));
97
102
        goto chpwfail;
98
103
    }
99
104
 
105
110
    ret = krb5_auth_con_init(context, &auth_context);
106
111
    if (ret) {
107
112
        numresult = KRB5_KPASSWD_HARDERROR;
108
 
        strcpy(strresult, "Failed initializing auth context");
 
113
        strlcpy(strresult, "Failed initializing auth context",
 
114
                sizeof(strresult));
109
115
        goto chpwfail;
110
116
    }
111
117
 
113
119
                                 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
114
120
    if (ret) {
115
121
        numresult = KRB5_KPASSWD_HARDERROR;
116
 
        strcpy(strresult, "Failed initializing auth context");
 
122
        strlcpy(strresult, "Failed initializing auth context",
 
123
                sizeof(strresult));
117
124
        goto chpwfail;
118
125
    }
119
126
        
121
128
                               "kadmin", "changepw", NULL);
122
129
    if (ret) {
123
130
        numresult = KRB5_KPASSWD_HARDERROR;
124
 
        strcpy(strresult, "Failed building kadmin/changepw principal");
 
131
        strlcpy(strresult, "Failed building kadmin/changepw principal",
 
132
                sizeof(strresult));
125
133
        goto chpwfail;
126
134
    }
127
135
 
130
138
 
131
139
    if (ret) {
132
140
        numresult = KRB5_KPASSWD_AUTHERROR;
133
 
        strcpy(strresult, "Failed reading application request");
134
 
        goto chpwfail;
135
 
    }
136
 
 
137
 
    /* set up address info */
138
 
 
139
 
    addrlen = sizeof(local_addr);
140
 
 
141
 
    if (getsockname(s, &local_addr, &addrlen) < 0) {
142
 
        ret = errno;
143
 
        numresult = KRB5_KPASSWD_HARDERROR;
144
 
        strcpy(strresult, "Failed getting server internet address");
145
 
        goto chpwfail;
146
 
    }
147
 
 
148
 
    /* some brain-dead OS's don't return useful information from
149
 
     * the getsockname call.  Namely, windows and solaris.  */
150
 
 
151
 
    if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
152
 
        local_kaddr.addrtype = ADDRTYPE_INET;
153
 
        local_kaddr.length =
154
 
            sizeof(((struct sockaddr_in *) &local_addr)->sin_addr);
155
 
        local_kaddr.contents = 
156
 
            (krb5_octet *) &(((struct sockaddr_in *) &local_addr)->sin_addr);
157
 
    } else {
158
 
        krb5_address **addrs;
159
 
 
160
 
        krb5_os_localaddr(context, &addrs);
161
 
        local_kaddr.magic = addrs[0]->magic;
162
 
        local_kaddr.addrtype = addrs[0]->addrtype;
163
 
        local_kaddr.length = addrs[0]->length;
164
 
        local_kaddr.contents = malloc(addrs[0]->length);
165
 
        memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length);
166
 
        allocated_mem++;
167
 
 
168
 
        krb5_free_addresses(context, addrs);
169
 
    }
170
 
 
171
 
    addrlen = sizeof(remote_addr);
172
 
 
173
 
    if (getpeername(s, &remote_addr, &addrlen) < 0) {
174
 
        ret = errno;
175
 
        numresult = KRB5_KPASSWD_HARDERROR;
176
 
        strcpy(strresult, "Failed getting client internet address");
177
 
        goto chpwfail;
178
 
    }
179
 
 
180
 
    remote_kaddr.addrtype = ADDRTYPE_INET;
181
 
    remote_kaddr.length =
182
 
        sizeof(((struct sockaddr_in *) &remote_addr)->sin_addr);
183
 
    remote_kaddr.contents = 
184
 
        (krb5_octet *) &(((struct sockaddr_in *) &remote_addr)->sin_addr);
185
 
    
186
 
    remote_kaddr.addrtype = ADDRTYPE_INET;
187
 
    remote_kaddr.length = sizeof(sockin->sin_addr);
188
 
    remote_kaddr.contents = (krb5_octet *) &sockin->sin_addr;
189
 
    
 
141
        strlcpy(strresult, "Failed reading application request",
 
142
                sizeof(strresult));
 
143
        goto chpwfail;
 
144
    }
 
145
 
190
146
    /* mk_priv requires that the local address be set.
191
147
       getsockname is used for this.  rd_priv requires that the
192
148
       remote address be set.  recvfrom is used for this.  If
202
158
       is specified.  Are we having fun yet?  */
203
159
 
204
160
    ret = krb5_auth_con_setaddrs(context, auth_context, NULL,
205
 
                             &remote_kaddr);
 
161
                                 remote_faddr->address);
206
162
    if (ret) {
207
163
        numresult = KRB5_KPASSWD_HARDERROR;
208
 
        strcpy(strresult, "Failed storing client internet address");
209
 
        goto chpwfail;
210
 
    }
211
 
 
212
 
    /* verify that this is an AS_REQ ticket */
213
 
 
214
 
    if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
215
 
        numresult = KRB5_KPASSWD_AUTHERROR;
216
 
        strcpy(strresult, "Ticket must be derived from a password");
 
164
        strlcpy(strresult, "Failed storing client internet address",
 
165
                sizeof(strresult));
217
166
        goto chpwfail;
218
167
    }
219
168
 
222
171
    ret = krb5_mk_rep(context, auth_context, &ap_rep);
223
172
    if (ret) {
224
173
        numresult = KRB5_KPASSWD_AUTHERROR;
225
 
        strcpy(strresult, "Failed replying to application request");
 
174
        strlcpy(strresult, "Failed replying to application request",
 
175
                sizeof(strresult));
226
176
        goto chpwfail;
227
177
    }
228
178
 
229
 
    /* decrypt the new password */
 
179
    /* decrypt the ChangePasswdData */
230
180
 
231
181
    cipher.length = (req->data + req->length) - ptr;
232
182
    cipher.data = ptr;
234
184
    ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
235
185
    if (ret) {
236
186
        numresult = KRB5_KPASSWD_HARDERROR;
237
 
        strcpy(strresult, "Failed decrypting request");
 
187
        strlcpy(strresult, "Failed decrypting request", sizeof(strresult));
238
188
        goto chpwfail;
239
189
    }
240
190
 
241
 
    ret = krb5_unparse_name(context, ticket->enc_part2->client, &clientstr);
 
191
    client = ticket->enc_part2->client;
 
192
 
 
193
    /* decode ChangePasswdData for setpw requests */
 
194
    if (vno == RFC3244_VERSION) {
 
195
        krb5_data *clear_data;
 
196
 
 
197
        ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
 
198
        if (ret != 0) {
 
199
            numresult = KRB5_KPASSWD_MALFORMED;
 
200
            strlcpy(strresult, "Failed decoding ChangePasswdData",
 
201
                    sizeof(strresult));
 
202
            goto chpwfail;
 
203
        }
 
204
 
 
205
        memset(clear.data, 0, clear.length);
 
206
        free(clear.data);
 
207
 
 
208
        clear = *clear_data;
 
209
        free(clear_data);
 
210
 
 
211
        if (target != NULL) {
 
212
            ret = krb5_unparse_name(context, target, &targetstr);
 
213
            if (ret != 0) {
 
214
                numresult = KRB5_KPASSWD_HARDERROR;
 
215
                strlcpy(strresult, "Failed unparsing target name for log",
 
216
                        sizeof(strresult));
 
217
                goto chpwfail;
 
218
            }
 
219
        }
 
220
    }
 
221
 
 
222
    ret = krb5_unparse_name(context, client, &clientstr);
242
223
    if (ret) {
243
224
        numresult = KRB5_KPASSWD_HARDERROR;
244
 
        strcpy(strresult, "Failed unparsing client name for log");
245
 
        goto chpwfail;
246
 
    }
 
225
        strlcpy(strresult, "Failed unparsing client name for log",
 
226
                sizeof(strresult));
 
227
        goto chpwfail;
 
228
    }
 
229
 
 
230
    /* for cpw, verify that this is an AS_REQ ticket */
 
231
    if (vno == 1 &&
 
232
        (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
 
233
        numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
 
234
        strlcpy(strresult, "Ticket must be derived from a password",
 
235
                sizeof(strresult));
 
236
        goto chpwfail;
 
237
    }
 
238
 
247
239
    /* change the password */
248
240
 
249
241
    ptr = (char *) malloc(clear.length+1);
250
242
    memcpy(ptr, clear.data, clear.length);
251
243
    ptr[clear.length] = '\0';
252
244
 
253
 
    ret = schpw_util_wrapper(server_handle, ticket->enc_part2->client,
 
245
    ret = schpw_util_wrapper(server_handle, client, target,
 
246
                             (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0,
254
247
                             ptr, NULL, strresult, sizeof(strresult));
 
248
    if (ret)
 
249
        errmsg = krb5_get_error_message(context, ret);
255
250
 
256
251
    /* zap the password */
257
252
    memset(clear.data, 0, clear.length);
258
253
    memset(ptr, 0, clear.length);
259
 
    krb5_xfree(clear.data);
 
254
    free(clear.data);
260
255
    free(ptr);
261
256
    clear.length = 0;
262
257
 
263
258
    clen = strlen(clientstr);
264
259
    trunc_name(&clen, &cdots);
265
 
    krb5_klog_syslog(LOG_NOTICE, "chpw request from %s for %.*s%s: %s",
266
 
                     inet_ntoa(((struct sockaddr_in *)&remote_addr)->sin_addr),
267
 
                     clen, clientstr, cdots,
268
 
                     ret ? krb5_get_error_message (context, ret) : "success");
269
 
    krb5_free_unparsed_name(context, clientstr);
270
 
 
271
 
    if (ret) {
272
 
        if ((ret != KADM5_PASS_Q_TOOSHORT) && 
273
 
            (ret != KADM5_PASS_REUSE) && (ret != KADM5_PASS_Q_CLASS) && 
274
 
            (ret != KADM5_PASS_Q_DICT) && (ret != KADM5_PASS_TOOSOON))
275
 
            numresult = KRB5_KPASSWD_HARDERROR;
276
 
        else
277
 
            numresult = KRB5_KPASSWD_SOFTERROR;
278
 
        /* strresult set by kadb5_chpass_principal_util() */
279
 
        goto chpwfail;
280
 
    }
281
 
 
282
 
    /* success! */
283
 
 
284
 
    numresult = KRB5_KPASSWD_SUCCESS;
285
 
    strcpy(strresult, "");
 
260
 
 
261
    switch (addr->addrtype) {
 
262
    case ADDRTYPE_INET: {
 
263
        struct sockaddr_in *sin = ss2sin(&ss);
 
264
 
 
265
        sin->sin_family = AF_INET;
 
266
        memcpy(&sin->sin_addr, addr->contents, addr->length);
 
267
        sin->sin_port = htons(remote_faddr->port);
 
268
        salen = sizeof(*sin);
 
269
        break;
 
270
    }
 
271
    case ADDRTYPE_INET6: {
 
272
        struct sockaddr_in6 *sin6 = ss2sin6(&ss);
 
273
 
 
274
        sin6->sin6_family = AF_INET6;
 
275
        memcpy(&sin6->sin6_addr, addr->contents, addr->length);
 
276
        sin6->sin6_port = htons(remote_faddr->port);
 
277
        salen = sizeof(*sin6);
 
278
        break;
 
279
    }
 
280
    default: {
 
281
        struct sockaddr *sa = ss2sa(&ss);
 
282
 
 
283
        sa->sa_family = AF_UNSPEC;
 
284
        salen = sizeof(*sa);
 
285
        break;
 
286
    }
 
287
    }
 
288
 
 
289
    if (getnameinfo(ss2sa(&ss), salen,
 
290
                    addrbuf, sizeof(addrbuf), NULL, 0,
 
291
                    NI_NUMERICHOST | NI_NUMERICSERV) != 0)
 
292
        strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf));
 
293
 
 
294
    if (vno == RFC3244_VERSION) {
 
295
        size_t tlen;
 
296
        char *tdots;
 
297
        const char *targetp;
 
298
 
 
299
        if (target == NULL) {
 
300
            tlen = clen;
 
301
            tdots = cdots;
 
302
            targetp = targetstr;
 
303
        } else {
 
304
            tlen = strlen(targetstr);
 
305
            trunc_name(&tlen, &tdots);
 
306
            targetp = clientstr;
 
307
        }
 
308
 
 
309
        krb5_klog_syslog(LOG_NOTICE, "setpw request from %s by %.*s%s for %.*s%s: %s",
 
310
                         addrbuf,
 
311
                         (int) clen, clientstr, cdots,
 
312
                         (int) tlen, targetp, tdots,
 
313
                         errmsg ? errmsg : "success");
 
314
    } else {
 
315
        krb5_klog_syslog(LOG_NOTICE, "chpw request from %s for %.*s%s: %s",
 
316
                         addrbuf,
 
317
                         (int) clen, clientstr, cdots,
 
318
                         errmsg ? errmsg : "success");
 
319
    }
 
320
    switch (ret) {
 
321
    case KADM5_AUTH_CHANGEPW:
 
322
        numresult = KRB5_KPASSWD_ACCESSDENIED;
 
323
        break;
 
324
    case KADM5_PASS_Q_TOOSHORT:
 
325
    case KADM5_PASS_REUSE:
 
326
    case KADM5_PASS_Q_CLASS:
 
327
    case KADM5_PASS_Q_DICT:
 
328
    case KADM5_PASS_TOOSOON:
 
329
        numresult = KRB5_KPASSWD_HARDERROR;
 
330
        break;
 
331
    case 0:
 
332
        numresult = KRB5_KPASSWD_SUCCESS;
 
333
        strlcpy(strresult, "", sizeof(strresult));
 
334
        break;
 
335
    default:
 
336
        numresult = KRB5_KPASSWD_SOFTERROR;
 
337
        break;
 
338
    }
286
339
 
287
340
chpwfail:
288
341
 
299
352
    cipher.length = 0;
300
353
 
301
354
    if (ap_rep.length) {
302
 
        ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr,
303
 
                                     NULL);
 
355
        ret = krb5_auth_con_setaddrs(context, auth_context,
 
356
                                     local_faddr->address, NULL);
304
357
        if (ret) {
305
358
            numresult = KRB5_KPASSWD_HARDERROR;
306
 
            strcpy(strresult,
307
 
                   "Failed storing client and server internet addresses");
 
359
            strlcpy(strresult,
 
360
                    "Failed storing client and server internet addresses",
 
361
                    sizeof(strresult));
308
362
        } else {
309
363
            ret = krb5_mk_priv(context, auth_context, &clear, &cipher,
310
364
                               &replay);
311
365
            if (ret) {
312
366
                numresult = KRB5_KPASSWD_HARDERROR;
313
 
                strcpy(strresult, "Failed encrypting reply");
 
367
                strlcpy(strresult, "Failed encrypting reply",
 
368
                        sizeof(strresult));
314
369
            }
315
370
        }
316
371
    }
323
378
           reply */
324
379
 
325
380
        if (ap_rep.length) {
326
 
            krb5_xfree(ap_rep.data);
 
381
            free(ap_rep.data);
327
382
            ap_rep.length = 0;
328
383
        }
329
384
 
363
418
 
364
419
    rep->length = 6 + ap_rep.length + cipher.length;
365
420
    rep->data = (char *) malloc(rep->length);
 
421
    if (rep->data == NULL) {
 
422
        rep->length = 0;        /* checked by caller */
 
423
        ret = ENOMEM;
 
424
        goto bailout;
 
425
    }
366
426
    ptr = rep->data;
367
427
 
368
428
    /* length */
397
457
    if (changepw)
398
458
        krb5_free_principal(context, changepw);
399
459
    if (ap_rep.length)
400
 
        krb5_xfree(ap_rep.data);
 
460
        free(ap_rep.data);
401
461
    if (ticket)
402
462
        krb5_free_ticket(context, ticket);
403
463
    if (clear.length)
404
 
        krb5_xfree(clear.data);
 
464
        free(clear.data);
405
465
    if (cipher.length)
406
 
        krb5_xfree(cipher.data);
407
 
    if (allocated_mem) 
408
 
        krb5_xfree(local_kaddr.contents);
 
466
        free(cipher.data);
 
467
    if (target)
 
468
        krb5_free_principal(context, target);
 
469
    if (targetstr)
 
470
        krb5_free_unparsed_name(context, targetstr);
 
471
    if (clientstr)
 
472
        krb5_free_unparsed_name(context, clientstr);
 
473
    if (errmsg)
 
474
        krb5_free_error_message(context, errmsg);
409
475
 
410
476
    return(ret);
411
477
}