2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 /* The rxkad security object. Authentication using a DES-encrypted
11 * Kerberos-style ticket. These are the server-only routines. */
14 #include <afsconfig.h>
15 #include <afs/param.h>
20 #include <sys/types.h>
25 #include <netinet/in.h>
30 #include <afs/afsutil.h>
31 #include "private_data.h"
32 #define XPRT_RXKAD_SERVER
35 * This can be set to allow alternate ticket decoding.
36 * Currently only used by the AFS/DFS protocol translator to recognize
37 * Kerberos V5 tickets. The actual code to do that is provided externally.
39 afs_int32 (*rxkad_AlternateTicketDecoder)();
41 static struct rx_securityOps rxkad_server_ops = {
44 rxkad_PreparePacket, /* once per packet creation */
45 0, /* send packet (once per retrans) */
46 rxkad_CheckAuthentication,
47 rxkad_CreateChallenge,
51 rxkad_CheckPacket, /* check data packet */
52 rxkad_DestroyConnection,
55 extern afs_uint32 rx_MyMaxSendSize;
57 /* Miscellaneous random number routines that use the fcrypt module and the
60 static fc_KeySchedule random_int32_schedule;
62 #ifdef AFS_PTHREAD_ENV
64 * This mutex protects the following global variables:
65 * random_int32_schedule
70 pthread_mutex_t rxkad_random_mutex;
71 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0);
72 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0);
76 #endif /* AFS_PTHREAD_ENV */
78 static void init_random_int32 ()
81 gettimeofday (&key, NULL);
83 fc_keysched (&key, random_int32_schedule);
87 static afs_int32 get_random_int32 ()
88 { static struct timeval seed;
92 fc_ecb_encrypt (&seed, &seed, random_int32_schedule, ENCRYPT);
98 /* Called with four parameters. The first is the level of encryption, as
99 defined in the rxkad.h file. The second and third are a rock and a
100 procedure that is called with the key version number that accompanies the
101 ticket and returns a pointer to the server's decryption key. The fourth
102 argument, if not NULL, is a pointer to a function that will be called for
103 every new connection with the name, instance and cell of the client. The
104 routine should return zero if the user is NOT acceptible to the server. If
105 this routine is not supplied, the server can call rxkad_GetServerInfo with
106 the rx connection pointer passed to the RPC routine to obtain information
109 struct rx_securityClass *
110 rxkad_NewServerSecurityObject (level, get_key_rock, get_key, user_ok)
111 rxkad_level level; /* minimum level */
112 char *get_key_rock; /* rock for get_key implementor */
113 int (*get_key)(); /* passed kvno & addr(key) to fill */
114 int (*user_ok)(); /* passed name, inst, cell => bool */
115 { struct rx_securityClass *tsc;
116 struct rxkad_sprivate *tsp;
119 if (!get_key) return 0;
121 size = sizeof(struct rx_securityClass);
122 tsc = (struct rx_securityClass *) osi_Alloc (size);
123 memset(tsc, 0, size);
124 tsc->refCount = 1; /* caller has one reference */
125 tsc->ops = &rxkad_server_ops;
126 size = sizeof(struct rxkad_sprivate);
127 tsp = (struct rxkad_sprivate *) osi_Alloc (size);
128 memset(tsp, 0, size);
129 tsc->privateData = (char *) tsp;
131 tsp->type |= rxkad_server; /* so can identify later */
132 tsp->level = level; /* level of encryption */
133 tsp->get_key_rock = get_key_rock;
134 tsp->get_key = get_key; /* to get server ticket */
135 tsp->user_ok = user_ok; /* to inform server of client id. */
136 init_random_int32 ();
139 rxkad_stats_serverObjects++;
144 /* server: called to tell if a connection authenticated properly */
146 rxs_return_t rxkad_CheckAuthentication (aobj, aconn)
147 struct rx_securityClass *aobj;
148 struct rx_connection *aconn;
149 { struct rxkad_sconn *sconn;
151 /* first make sure the object exists */
152 if (!aconn->securityData) return RXKADINCONSISTENCY;
154 sconn = (struct rxkad_sconn *) aconn->securityData;
155 return !sconn->authenticated;
158 /* server: put the current challenge in the connection structure for later use
161 rxs_return_t rxkad_CreateChallenge(aobj, aconn)
162 struct rx_securityClass *aobj;
163 struct rx_connection *aconn;
164 { struct rxkad_sconn *sconn;
165 struct rxkad_sprivate *tsp;
167 sconn = (struct rxkad_sconn *) aconn->securityData;
168 sconn->challengeID = get_random_int32 ();
169 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
170 /* initialize level from object's minimum acceptable level */
171 tsp = (struct rxkad_sprivate *)aobj->privateData;
172 sconn->level = tsp->level;
176 /* server: fill in a challenge in the packet */
178 rxs_return_t rxkad_GetChallenge (aobj, aconn, apacket)
179 IN struct rx_securityClass *aobj;
180 IN struct rx_packet *apacket;
181 IN struct rx_connection *aconn;
182 { struct rxkad_sconn *sconn;
186 struct rxkad_v2Challenge c_v2; /* version 2 */
187 struct rxkad_oldChallenge c_old; /* old style */
189 sconn = (struct rxkad_sconn *) aconn->securityData;
190 if (rx_IsUsingPktCksum(aconn)) sconn->cksumSeen = 1;
192 if (sconn->cksumSeen) {
193 memset(&c_v2, 0, sizeof(c_v2));
194 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
195 c_v2.challengeID = htonl(sconn->challengeID);
196 c_v2.level = htonl((afs_int32)sconn->level);
198 challenge = (char *)&c_v2;
199 challengeSize = sizeof(c_v2);
201 memset(&c_old, 0, sizeof(c_old));
202 c_old.challengeID = htonl(sconn->challengeID);
203 c_old.level = htonl((afs_int32)sconn->level);
204 challenge = (char *)&c_old;
205 challengeSize = sizeof(c_old);
207 if (rx_MyMaxSendSize < challengeSize)
208 return RXKADPACKETSHORT; /* not enough space */
210 rx_packetwrite(apacket, 0, challengeSize, challenge);
211 rx_SetDataSize (apacket, challengeSize);
214 rxkad_stats.challengesSent++;
219 /* server: process a response to a challenge packet */
220 /* XXX this does some copying of data in and out of the packet, but I'll bet it
221 * could just do it in place, especially if I used rx_Pullup...
223 rxs_return_t rxkad_CheckResponse (aobj, aconn, apacket)
224 struct rx_securityClass *aobj;
225 struct rx_packet *apacket;
226 struct rx_connection *aconn;
227 { struct rxkad_sconn *sconn;
228 struct rxkad_sprivate *tsp;
229 struct ktc_encryptionKey serverKey;
230 struct rxkad_oldChallengeResponse oldr; /* response format */
231 struct rxkad_v2ChallengeResponse v2r;
232 afs_int32 tlen; /* ticket len */
233 afs_int32 kvno; /* key version of ticket */
234 char tix[MAXKTCTICKETLEN];
235 afs_int32 incChallengeID;
238 /* ticket contents */
239 struct ktc_principal client;
240 struct ktc_encryptionKey sessionkey;
245 struct rxkad_serverinfo *rock;
247 sconn = (struct rxkad_sconn *) aconn->securityData;
248 tsp = (struct rxkad_sprivate *) aobj->privateData;
250 if (sconn->cksumSeen) {
251 /* expect v2 response, leave fields in v2r in network order for cksum
252 * computation which follows decryption. */
253 if (rx_GetDataSize(apacket) < sizeof(v2r))
254 return RXKADPACKETSHORT;
255 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
259 kvno = ntohl (v2r.kvno);
260 tlen = ntohl(v2r.ticketLen);
261 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
262 return RXKADPACKETSHORT;
264 /* expect old format response */
265 if (rx_GetDataSize(apacket) < sizeof(oldr)) return RXKADPACKETSHORT;
266 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
269 kvno = ntohl (oldr.kvno);
270 tlen = ntohl(oldr.ticketLen);
271 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
272 return RXKADPACKETSHORT;
274 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
275 return RXKADTICKETLEN;
277 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
280 * We allow the ticket to be optionally decoded by an alternate
281 * ticket decoder, if the function variable
282 * rxkad_AlternateTicketDecoder is set. That function should
283 * return a code of -1 if it wants the ticket to be decoded by
284 * the standard decoder.
286 if (rxkad_AlternateTicketDecoder) {
287 code = rxkad_AlternateTicketDecoder
288 (kvno, tix, tlen, client.name, client.instance, client.cell,
289 &sessionkey, &host, &start, &end);
290 if (code && code != -1) {
294 code = -1; /* No alternate ticket decoder present */
298 * If the alternate decoder is not present, or returns -1, then
299 * assume the ticket is of the default style.
302 /* get ticket's key */
303 code = (*tsp->get_key)(tsp->get_key_rock, kvno, &serverKey);
304 if (code) return RXKADUNKNOWNKEY; /* invalid kvno */
305 code = tkt_DecodeTicket (tix, tlen, &serverKey,
306 client.name, client.instance, client.cell,
307 &sessionkey, &host, &start, &end);
308 if (code) return RXKADBADTICKET;
310 code = tkt_CheckTimes (start, end, time(0));
311 if (code == -1) return RXKADEXPIRED;
312 else if (code <= 0) return RXKADNOAUTH;
314 code = fc_keysched (&sessionkey, sconn->keysched);
315 if (code) return RXKADBADKEY;
316 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
318 if (sconn->cksumSeen) {
319 /* using v2 response */
320 afs_uint32 cksum; /* observed cksum */
321 struct rxkad_endpoint endpoint; /* connections endpoint */
325 memcpy(xor, sconn->ivec, 2*sizeof(afs_int32));
326 fc_cbc_encrypt (&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
327 sconn->keysched, xor, DECRYPT);
328 cksum = rxkad_CksumChallengeResponse (&v2r);
329 if (cksum != v2r.encrypted.endpoint.cksum)
330 return RXKADSEALEDINCON;
331 (void) rxkad_SetupEndpoint (aconn, &endpoint);
332 v2r.encrypted.endpoint.cksum = 0;
333 if (memcmp (&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
334 return RXKADSEALEDINCON;
335 for (i=0; i<RX_MAXCALLS; i++) {
336 v2r.encrypted.callNumbers[i] = ntohl(v2r.encrypted.callNumbers[i]);
337 if (v2r.encrypted.callNumbers[i] < 0) return RXKADSEALEDINCON;
340 (void) rxi_SetCallNumberVector (aconn, v2r.encrypted.callNumbers);
341 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
342 level = ntohl(v2r.encrypted.level);
344 /* expect old format response */
345 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted,
346 sconn->keysched, DECRYPT);
347 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
348 level = ntohl(oldr.encrypted.level);
350 if (incChallengeID != sconn->challengeID+1)
351 return RXKADOUTOFSEQUENCE; /* replay attempt */
352 if ((level < sconn->level) || (level > rxkad_crypt)) return RXKADLEVELFAIL;
353 sconn->level = level;
354 rxkad_SetLevel (aconn, sconn->level);
356 rxkad_stats.responses[rxkad_LevelIndex(sconn->level)]++;
359 /* now compute endpoint-specific info used for computing 16 bit checksum */
360 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
362 /* otherwise things are ok */
363 sconn->expirationTime = end;
364 sconn->authenticated = 1;
367 code = tsp->user_ok (client.name, client.instance, client.cell, kvno);
368 if (code) return RXKADNOAUTH;
370 else { /* save the info for later retreival */
371 int size = sizeof(struct rxkad_serverinfo);
372 rock = (struct rxkad_serverinfo *) osi_Alloc (size);
373 memset(rock, 0, size);
375 memcpy(&rock->client, &client, sizeof(rock->client));
381 /* return useful authentication info about a server-side connection */
383 afs_int32 rxkad_GetServerInfo (aconn, level, expiration,
384 name, instance, cell, kvno)
385 struct rx_connection *aconn;
387 afs_uint32 *expiration;
393 struct rxkad_sconn *sconn;
395 sconn = (struct rxkad_sconn *) aconn->securityData;
396 if (sconn && sconn->authenticated && sconn->rock &&
397 (time(0) < sconn->expirationTime)) {
398 if (level) *level = sconn->level;
399 if (expiration) *expiration = sconn->expirationTime;
400 if (name) strcpy (name, sconn->rock->client.name);
401 if (instance) strcpy (instance, sconn->rock->client.instance);
402 if (cell) strcpy (cell, sconn->rock->client.cell);
403 if (kvno) *kvno = sconn->rock->kvno;
406 else return RXKADNOAUTH;