Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / krb / rd_cred.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/rd_cred.c - definition of krb5_rd_cred() */
3 /*
4  * Copyright 1994-2009,2014 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "k5-int.h"
28 #include "cleanup.h"
29 #include "auth_con.h"
30
31 #include <stdlib.h>
32 #include <errno.h>
33
34 /*
35  * Decrypt and decode the enc_part of a krb5_cred using the receiving subkey or
36  * the session key of authcon.  If neither key is present, ctext->ciphertext is
37  * assumed to be unencrypted plain text.
38  */
39 static krb5_error_code
40 decrypt_encpart(krb5_context context, krb5_enc_data *ctext,
41                 krb5_auth_context authcon, krb5_cred_enc_part **encpart_out)
42 {
43     krb5_error_code ret;
44     krb5_data plain = empty_data();
45     krb5_boolean decrypted = FALSE;
46
47     *encpart_out = NULL;
48
49     if (authcon->recv_subkey == NULL && authcon->key == NULL)
50         return decode_krb5_enc_cred_part(&ctext->ciphertext, encpart_out);
51
52     ret = alloc_data(&plain, ctext->ciphertext.length);
53     if (ret)
54         return ret;
55     if (authcon->recv_subkey != NULL) {
56         ret = krb5_k_decrypt(context, authcon->recv_subkey,
57                              KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0, ctext, &plain);
58         decrypted = (ret == 0);
59     }
60     if (!decrypted && authcon->key != NULL) {
61         ret = krb5_k_decrypt(context, authcon->key,
62                              KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0, ctext, &plain);
63         decrypted = (ret == 0);
64     }
65     if (decrypted)
66         ret = decode_krb5_enc_cred_part(&plain, encpart_out);
67     zapfree(plain.data, plain.length);
68     return ret;
69 }
70
71 /* Produce a list of credentials from a KRB-CRED message and its enc_part. */
72 static krb5_error_code
73 make_cred_list(krb5_context context, krb5_cred *krbcred,
74                krb5_cred_enc_part *encpart, krb5_creds ***creds_out)
75 {
76     krb5_error_code ret = 0;
77     krb5_creds **list = NULL;
78     krb5_cred_info *info;
79     krb5_data *ticket_data;
80     size_t i, count;
81
82     *creds_out = NULL;
83
84     /* Allocate the list of creds. */
85     for (count = 0; krbcred->tickets[count] != NULL; count++);
86     list = k5calloc(count + 1, sizeof(*list), &ret);
87     if (list == NULL)
88         goto cleanup;
89
90     /* For each credential, create a strcture in the list of credentials and
91      * copy the information. */
92     for (i = 0; i < count; i++) {
93         list[i] = k5alloc(sizeof(*list[i]), &ret);
94         if (list[i] == NULL)
95             goto cleanup;
96
97         info = encpart->ticket_info[i];
98         ret = krb5_copy_principal(context, info->client, &list[i]->client);
99         if (ret)
100             goto cleanup;
101
102         ret = krb5_copy_principal(context, info->server, &list[i]->server);
103         if (ret)
104             goto cleanup;
105
106         ret = krb5_copy_keyblock_contents(context, info->session,
107                                           &list[i]->keyblock);
108         if (ret)
109             goto cleanup;
110
111         ret = krb5_copy_addresses(context, info->caddrs, &list[i]->addresses);
112         if (ret)
113             goto cleanup;
114
115         ret = encode_krb5_ticket(krbcred->tickets[i], &ticket_data);
116         if (ret)
117             goto cleanup;
118         list[i]->ticket = *ticket_data;
119         free(ticket_data);
120
121         list[i]->is_skey = FALSE;
122         list[i]->magic = KV5M_CREDS;
123         list[i]->times = info->times;
124         list[i]->ticket_flags = info->flags;
125         list[i]->authdata = NULL;
126         list[i]->second_ticket = empty_data();
127     }
128
129     *creds_out = list;
130     list = NULL;
131
132 cleanup:
133     krb5_free_tgt_creds(context, list);
134     return ret;
135 }
136
137 /* Validate a KRB-CRED message in creddata, and return a list of forwarded
138  * credentials along with replay cache information. */
139 krb5_error_code KRB5_CALLCONV
140 krb5_rd_cred(krb5_context context, krb5_auth_context authcon,
141              krb5_data *creddata, krb5_creds ***creds_out,
142              krb5_replay_data *replaydata_out)
143 {
144     krb5_error_code ret = 0;
145     krb5_creds **credlist = NULL;
146     krb5_cred *krbcred = NULL;
147     krb5_cred_enc_part *encpart = NULL;
148     krb5_donot_replay replay;
149     const krb5_int32 flags = authcon->auth_context_flags;
150
151     *creds_out = NULL;
152
153     if (((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
154          (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
155         replaydata_out == NULL)
156         return KRB5_RC_REQUIRED;
157
158     if ((flags & KRB5_AUTH_CONTEXT_DO_TIME) && authcon->rcache == NULL)
159         return KRB5_RC_REQUIRED;
160
161     ret = decode_krb5_cred(creddata, &krbcred);
162     if (ret)
163         goto cleanup;
164
165     ret = decrypt_encpart(context, &krbcred->enc_part, authcon, &encpart);
166     if (ret)
167         goto cleanup;
168
169     ret = make_cred_list(context, krbcred, encpart, &credlist);
170     if (ret)
171         goto cleanup;
172
173     if (flags & KRB5_AUTH_CONTEXT_DO_TIME) {
174         ret = krb5_check_clockskew(context, encpart->timestamp);
175         if (ret)
176             goto cleanup;
177
178         ret = krb5_gen_replay_name(context, authcon->remote_addr, "_forw",
179                                    &replay.client);
180         if (ret)
181             goto cleanup;
182
183         replay.server = "";
184         replay.msghash = NULL;
185         replay.cusec = encpart->usec;
186         replay.ctime = encpart->timestamp;
187         ret = krb5_rc_store(context, authcon->rcache, &replay);
188         free(replay.client);
189         if (ret)
190             goto cleanup;
191     }
192
193     if (flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
194         if (authcon->remote_seq_number != (uint32_t)encpart->nonce) {
195             ret = KRB5KRB_AP_ERR_BADORDER;
196             goto cleanup;
197         }
198         authcon->remote_seq_number++;
199     }
200
201     *creds_out = credlist;
202     credlist = NULL;
203     if ((flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
204         (flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
205         replaydata_out->timestamp = encpart->timestamp;
206         replaydata_out->usec = encpart->usec;
207         replaydata_out->seq = encpart->nonce;
208     }
209
210 cleanup:
211     krb5_free_tgt_creds(context, credlist);
212     krb5_free_cred(context, krbcred);
213     krb5_free_cred_enc_part(context, encpart);
214     free(encpart);              /* krb5_free_cred_enc_part doesn't do this */
215     return ret;
216 }