summaryrefslogtreecommitdiffstats
path: root/src/lib/krb5/krb/rd_req_dec.c
blob: c03df28ea62b116e78a2764cd895d0a6206cb085 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*
 * $Source$
 * $Author$
 *
 * Copyright 1990 by the Massachusetts Institute of Technology.
 *
 * For copying and distribution information, please see the file
 * <krb5/copyright.h>.
 *
 * krb5_rd_req_decoded()
 */

#if !defined(lint) && !defined(SABER)
static char rd_req_dec_c[] =
"$Id$";
#endif	/* !lint & !SABER */

#include <krb5/copyright.h>
#include <krb5/krb5.h>
#include <krb5/ext-proto.h>
#include <krb5/libos-proto.h>
#include <krb5/asn1.h>

/*
 essentially the same as krb_rd_req, but uses a decoded AP_REQ as the input
 rather than an encoded input.
 */
/*
 Parses a KRB_AP_REQ message, returning its contents.

 server specifies the expected server's name for the ticket.

 sender_addr specifies the address(es) expected to be present in the
 ticket.

 rcache specifies a replay detection cache used to store authenticators and
 server names

 keyproc specifies a procedure to generate a decryption key for the
 ticket.  If keyproc is non-NULL, keyprocarg is passed to it, and the result
 used as a decryption key. If keyproc is NULL, then fetchfrom is checked;
 if it is non-NULL, it specifies a parameter name from which to retrieve the
 decryption key.  If fetchfrom is NULL, then the default key store is
 consulted.

 authdat->ticket and authdat->authenticator are set to allocated storage
 structures; the caller should free them when finished.

 returns system errors, encryption errors, replay errors
 */

static krb5_error_code decrypt_authenticator PROTOTYPE((const krb5_ap_req *,
							krb5_authenticator **));
extern krb5_deltat krb5_clockskew;
#define in_clock_skew(date) (abs((date)-currenttime) < krb5_clockskew)

krb5_error_code
krb5_rd_req_decoded(req, server, sender_addr, fetchfrom, keyproc, keyprocarg,
		    rcache, tktauthent)
const krb5_ap_req *req;
const krb5_principal server;
const krb5_address *sender_addr;
const krb5_pointer fetchfrom;
krb5_error_code (*keyproc) PROTOTYPE((krb5_pointer,
				      krb5_principal,
				      krb5_kvno,
				      krb5_keyblock **));
const krb5_pointer keyprocarg;
krb5_rcache rcache;
krb5_tkt_authent *tktauthent;
{
    krb5_error_code retval;
    krb5_keyblock *tkt_key;
    krb5_keyblock tkt_key_real;
    krb5_timestamp currenttime;


    if (!krb5_principal_compare(server, req->ticket->server))
	return KRB5KRB_AP_WRONG_PRINC;

    /* if (req->ap_options & AP_OPTS_USE_SESSION_KEY)
       do we need special processing here ?	*/
    
    /* fetch a server key */
    if (keyproc) {
	retval = (*keyproc)(keyprocarg, req->ticket->server,
			    req->ticket->skvno, &tkt_key);
    } else {
	krb5_keytab keytabid;
	krb5_keytab_entry ktentry;

	if (fetchfrom) {
	    /* use the named keytab */
	    retval = krb5_kt_resolve((char *)fetchfrom, &keytabid);
	} else {
	    /* use default keytab */
	    retval = krb5_kt_default(&keytabid);
	}
	if (!retval) {
	    retval = krb5_kt_get_entry(keytabid, req->ticket->server,
				       req->ticket->skvno, &ktentry);
	    (void) krb5_kt_close(keytabid);
	    if (!retval) {
		retval = krb5_copy_keyblock(&ktentry.key, &tkt_key_real);
		tkt_key = &tkt_key_real;
		(void) krb5_kt_free_entry(&ktentry);
	    }
	}
    }
    if (retval)
	return retval;			/* some error in getting the key */

    /* decrypt the ticket */
    if (retval = krb5_decrypt_tkt_part(tkt_key, req->ticket))
	return(retval);

#define clean_ticket() krb5_free_enc_tkt_part(req->ticket->enc_part2)

    if (retval = decrypt_authenticator(req, &tktauthent->authenticator)) {
	clean_ticket();
	return retval;
    }
#define clean_authenticator() {(void) krb5_free_authenticator(tktauthent->authenticator); tktauthent->authenticator = 0;}

    if (!krb5_principal_compare(tktauthent->authenticator->client,
				req->ticket->enc_part2->client)) {
	clean_authenticator();
	return KRB5KRB_AP_ERR_BADMATCH;
    }
    if (sender_addr && !krb5_address_search(sender_addr, req->ticket->enc_part2->caddrs)) {
	clean_authenticator();
	return KRB5KRB_AP_ERR_BADADDR;
    }

    if (retval = krb5_timeofday(&currenttime)) {
	clean_authenticator();
	return retval;
    }
    if (!in_clock_skew(tktauthent->authenticator->ctime)) {
	clean_authenticator();
	return KRB5KRB_AP_ERR_SKEW;
    }

    tktauthent->ticket = req->ticket;	/* only temporarily...allocated
					   below */

    if ((retval = krb5_rc_store(rcache, tktauthent))) {
	tktauthent->ticket = 0;
	clean_authenticator();
	return retval;
    }
    tktauthent->ticket = 0;
    if (req->ticket->enc_part2->times.starttime - currenttime > krb5_clockskew) {
	clean_authenticator();
	return KRB5KRB_AP_ERR_TKT_NYV;	/* ticket not yet valid */
    }	
    if (currenttime - req->ticket->enc_part2->times.endtime > krb5_clockskew) {
	clean_authenticator();
	return KRB5KRB_AP_ERR_TKT_EXPIRED;	/* ticket expired */
    }	
    if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) {
	clean_authenticator();
	return KRB5KRB_AP_ERR_TKT_INVALID;
    }
    if (retval = krb5_copy_ticket(req->ticket, &tktauthent->ticket)) {
	clean_authenticator();
    } else
	tktauthent->ap_options = req->ap_options;
    return retval;
}

static krb5_error_code
decrypt_authenticator(request, authpp)
const krb5_ap_req *request;
krb5_authenticator **authpp;
{
    krb5_authenticator *local_auth;
    krb5_error_code retval;
    krb5_encrypt_block eblock;
    krb5_data scratch;
    krb5_keyblock *sesskey;

    sesskey = request->ticket->enc_part2->session;

    if (!valid_keytype(sesskey->keytype))
	return KRB5_PROG_KEYTYPE_NOSUPP;

    /* put together an eblock for this encryption */

    eblock.crypto_entry = krb5_keytype_array[sesskey->keytype]->system;

    scratch.length = request->authenticator.length;
    if (!(scratch.data = malloc(scratch.length)))
	return(ENOMEM);

    /* do any necessary key pre-processing */
    if (retval = (*eblock.crypto_entry->process_key)(&eblock, sesskey)) {
	free(scratch.data);
	return(retval);
    }

    /* call the encryption routine */
    if (retval =
	(*eblock.crypto_entry->decrypt_func)((krb5_pointer) request->authenticator.data,
					     (krb5_pointer) scratch.data,
					     scratch.length, &eblock, 0)) {
	(void) (*eblock.crypto_entry->finish_key)(&eblock);
	free(scratch.data);
	return retval;
    }
#define clean_scratch() {bzero(scratch.data, scratch.length); free(scratch.data);}
    if (retval = (*eblock.crypto_entry->finish_key)(&eblock)) {

	clean_scratch();
	return retval;
    }
    /*  now decode the decrypted stuff */
    if (!(retval = decode_krb5_authenticator(&scratch, &local_auth))) {
	*authpp = local_auth;
    }
    clean_scratch();
    return retval;
}