1 /* The rxkad security object. Authentication using a DES-encrypted
2 * Kerberos-style ticket. These are the server-only routines. */
4 /* Copyright (C) 1991, 1989 Transarc Corporation - All rights reserved */
6 ****************************************************************************
7 * Copyright IBM Corporation 1988, 1989 - All Rights Reserved *
9 * Permission to use, copy, modify, and distribute this software and its *
10 * documentation for any purpose and without fee is hereby granted, *
11 * provided that the above copyright notice appear in all copies and *
12 * that both that copyright notice and this permission notice appear in *
13 * supporting documentation, and that the name of IBM not be used in *
14 * advertising or publicity pertaining to distribution of the software *
15 * without specific, written prior permission. *
17 * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL *
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM *
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY *
20 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER *
21 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING *
22 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
23 ****************************************************************************
26 #include <afs/param.h>
28 #include <sys/types.h>
33 #include <netinet/in.h>
38 #include <afs/afsutil.h>
39 #include "private_data.h"
40 #define XPRT_RXKAD_SERVER
41 #include "../permit_xprt.h"
45 * This can be set to allow alternate ticket decoding.
46 * Currently only used by the AFS/DFS protocol translator to recognize
47 * Kerberos V5 tickets. The actual code to do that is provided externally.
49 afs_int32 (*rxkad_AlternateTicketDecoder)();
51 static struct rx_securityOps rxkad_server_ops = {
54 rxkad_PreparePacket, /* once per packet creation */
55 0, /* send packet (once per retrans) */
56 rxkad_CheckAuthentication,
57 rxkad_CreateChallenge,
61 rxkad_CheckPacket, /* check data packet */
62 rxkad_DestroyConnection,
65 extern afs_uint32 rx_MyMaxSendSize;
67 /* Miscellaneous random number routines that use the fcrypt module and the
70 static fc_KeySchedule random_int32_schedule;
72 #ifdef AFS_PTHREAD_ENV
74 * This mutex protects the following global variables:
75 * random_int32_schedule
80 pthread_mutex_t rxkad_random_mutex;
81 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0);
82 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0);
86 #endif /* AFS_PTHREAD_ENV */
88 static void init_random_int32 ()
91 gettimeofday (&key, (char *) 0);
93 fc_keysched (&key, random_int32_schedule);
97 static afs_int32 get_random_int32 ()
98 { static struct timeval seed;
102 fc_ecb_encrypt (&seed, &seed, random_int32_schedule, ENCRYPT);
108 /* Called with four parameters. The first is the level of encryption, as
109 defined in the rxkad.h file. The second and third are a rock and a
110 procedure that is called with the key version number that accompanies the
111 ticket and returns a pointer to the server's decryption key. The fourth
112 argument, if not NULL, is a pointer to a function that will be called for
113 every new connection with the name, instance and cell of the client. The
114 routine should return zero if the user is NOT acceptible to the server. If
115 this routine is not supplied, the server can call rxkad_GetServerInfo with
116 the rx connection pointer passed to the RPC routine to obtain information
119 struct rx_securityClass *
120 rxkad_NewServerSecurityObject (level, get_key_rock, get_key, user_ok)
121 rxkad_level level; /* minimum level */
122 char *get_key_rock; /* rock for get_key implementor */
123 int (*get_key)(); /* passed kvno & addr(key) to fill */
124 int (*user_ok)(); /* passed name, inst, cell => bool */
125 { struct rx_securityClass *tsc;
126 struct rxkad_sprivate *tsp;
129 if (!get_key) return 0;
131 size = sizeof(struct rx_securityClass);
132 tsc = (struct rx_securityClass *) osi_Alloc (size);
134 tsc->refCount = 1; /* caller has one reference */
135 tsc->ops = &rxkad_server_ops;
136 size = sizeof(struct rxkad_sprivate);
137 tsp = (struct rxkad_sprivate *) osi_Alloc (size);
139 tsc->privateData = (char *) tsp;
141 tsp->type |= rxkad_server; /* so can identify later */
142 tsp->level = level; /* level of encryption */
143 tsp->get_key_rock = get_key_rock;
144 tsp->get_key = get_key; /* to get server ticket */
145 tsp->user_ok = user_ok; /* to inform server of client id. */
146 init_random_int32 ();
149 rxkad_stats_serverObjects++;
154 /* server: called to tell if a connection authenticated properly */
156 rxs_return_t rxkad_CheckAuthentication (aobj, aconn)
157 struct rx_securityClass *aobj;
158 struct rx_connection *aconn;
159 { struct rxkad_sconn *sconn;
161 /* first make sure the object exists */
162 if (!aconn->securityData) return RXKADINCONSISTENCY;
164 sconn = (struct rxkad_sconn *) aconn->securityData;
165 return !sconn->authenticated;
168 /* server: put the current challenge in the connection structure for later use
171 rxs_return_t rxkad_CreateChallenge(aobj, aconn)
172 struct rx_securityClass *aobj;
173 struct rx_connection *aconn;
174 { struct rxkad_sconn *sconn;
175 struct rxkad_sprivate *tsp;
177 sconn = (struct rxkad_sconn *) aconn->securityData;
178 sconn->challengeID = get_random_int32 ();
179 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
180 /* initialize level from object's minimum acceptable level */
181 tsp = (struct rxkad_sprivate *)aobj->privateData;
182 sconn->level = tsp->level;
186 /* server: fill in a challenge in the packet */
188 rxs_return_t rxkad_GetChallenge (aobj, aconn, apacket)
189 IN struct rx_securityClass *aobj;
190 IN struct rx_packet *apacket;
191 IN struct rx_connection *aconn;
192 { struct rxkad_sconn *sconn;
196 struct rxkad_v2Challenge c_v2; /* version 2 */
197 struct rxkad_oldChallenge c_old; /* old style */
199 sconn = (struct rxkad_sconn *) aconn->securityData;
200 if (rx_IsUsingPktCksum(aconn)) sconn->cksumSeen = 1;
202 if (sconn->cksumSeen) {
203 bzero (&c_v2, sizeof(c_v2));
204 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
205 c_v2.challengeID = htonl(sconn->challengeID);
206 c_v2.level = htonl((afs_int32)sconn->level);
208 challenge = (char *)&c_v2;
209 challengeSize = sizeof(c_v2);
211 bzero (&c_old, sizeof(c_old));
212 c_old.challengeID = htonl(sconn->challengeID);
213 c_old.level = htonl((afs_int32)sconn->level);
214 challenge = (char *)&c_old;
215 challengeSize = sizeof(c_old);
217 if (rx_MyMaxSendSize < challengeSize)
218 return RXKADPACKETSHORT; /* not enough space */
220 rx_packetwrite(apacket, 0, challengeSize, challenge);
221 rx_SetDataSize (apacket, challengeSize);
224 rxkad_stats.challengesSent++;
229 /* server: process a response to a challenge packet */
230 /* XXX this does some copying of data in and out of the packet, but I'll bet it
231 * could just do it in place, especially if I used rx_Pullup...
233 rxs_return_t rxkad_CheckResponse (aobj, aconn, apacket)
234 struct rx_securityClass *aobj;
235 struct rx_packet *apacket;
236 struct rx_connection *aconn;
237 { struct rxkad_sconn *sconn;
238 struct rxkad_sprivate *tsp;
239 struct ktc_encryptionKey serverKey;
240 struct rxkad_oldChallengeResponse oldr; /* response format */
241 struct rxkad_v2ChallengeResponse v2r;
242 afs_int32 tlen; /* ticket len */
243 afs_int32 kvno; /* key version of ticket */
244 char tix[MAXKTCTICKETLEN];
245 afs_int32 incChallengeID;
248 /* ticket contents */
249 struct ktc_principal client;
250 struct ktc_encryptionKey sessionkey;
255 struct rxkad_serverinfo *rock;
257 sconn = (struct rxkad_sconn *) aconn->securityData;
258 tsp = (struct rxkad_sprivate *) aobj->privateData;
260 if (sconn->cksumSeen) {
261 /* expect v2 response, leave fields in v2r in network order for cksum
262 * computation which follows decryption. */
263 if (rx_GetDataSize(apacket) < sizeof(v2r))
264 return RXKADPACKETSHORT;
265 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
269 kvno = ntohl (v2r.kvno);
270 tlen = ntohl(v2r.ticketLen);
271 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
272 return RXKADPACKETSHORT;
274 /* expect old format response */
275 if (rx_GetDataSize(apacket) < sizeof(oldr)) return RXKADPACKETSHORT;
276 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
279 kvno = ntohl (oldr.kvno);
280 tlen = ntohl(oldr.ticketLen);
281 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
282 return RXKADPACKETSHORT;
284 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
285 return RXKADTICKETLEN;
287 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
290 * We allow the ticket to be optionally decoded by an alternate
291 * ticket decoder, if the function variable
292 * rxkad_AlternateTicketDecoder is set. That function should
293 * return a code of -1 if it wants the ticket to be decoded by
294 * the standard decoder.
296 if (rxkad_AlternateTicketDecoder) {
297 code = rxkad_AlternateTicketDecoder
298 (kvno, tix, tlen, client.name, client.instance, client.cell,
299 &sessionkey, &host, &start, &end);
300 if (code && code != -1) {
304 code = -1; /* No alternate ticket decoder present */
308 * If the alternate decoder is not present, or returns -1, then
309 * assume the ticket is of the default style.
312 /* get ticket's key */
313 code = (*tsp->get_key)(tsp->get_key_rock, kvno, &serverKey);
314 if (code) return RXKADUNKNOWNKEY; /* invalid kvno */
315 code = tkt_DecodeTicket (tix, tlen, &serverKey,
316 client.name, client.instance, client.cell,
317 &sessionkey, &host, &start, &end);
318 if (code) return RXKADBADTICKET;
320 code = tkt_CheckTimes (start, end, time(0));
321 if (code == -1) return RXKADEXPIRED;
322 else if (code <= 0) return RXKADNOAUTH;
324 code = fc_keysched (&sessionkey, sconn->keysched);
325 if (code) return RXKADBADKEY;
326 bcopy (&sessionkey, sconn->ivec, sizeof(sconn->ivec));
328 if (sconn->cksumSeen) {
329 /* using v2 response */
330 afs_uint32 cksum; /* observed cksum */
331 struct rxkad_endpoint endpoint; /* connections endpoint */
335 bcopy(sconn->ivec, xor, 2*sizeof(afs_int32));
336 fc_cbc_encrypt (&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
337 sconn->keysched, xor, DECRYPT);
338 cksum = rxkad_CksumChallengeResponse (&v2r);
339 if (cksum != v2r.encrypted.endpoint.cksum)
340 return RXKADSEALEDINCON;
341 (void) rxkad_SetupEndpoint (aconn, &endpoint);
342 v2r.encrypted.endpoint.cksum = 0;
343 if (bcmp (&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
344 return RXKADSEALEDINCON;
345 for (i=0; i<RX_MAXCALLS; i++) {
346 v2r.encrypted.callNumbers[i] = ntohl(v2r.encrypted.callNumbers[i]);
347 if (v2r.encrypted.callNumbers[i] < 0) return RXKADSEALEDINCON;
350 (void) rxi_SetCallNumberVector (aconn, v2r.encrypted.callNumbers);
351 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
352 level = ntohl(v2r.encrypted.level);
354 /* expect old format response */
355 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted,
356 sconn->keysched, DECRYPT);
357 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
358 level = ntohl(oldr.encrypted.level);
360 if (incChallengeID != sconn->challengeID+1)
361 return RXKADOUTOFSEQUENCE; /* replay attempt */
362 if ((level < sconn->level) || (level > rxkad_crypt)) return RXKADLEVELFAIL;
363 sconn->level = level;
364 rxkad_SetLevel (aconn, sconn->level);
366 rxkad_stats.responses[rxkad_LevelIndex(sconn->level)]++;
369 /* now compute endpoint-specific info used for computing 16 bit checksum */
370 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
372 /* otherwise things are ok */
373 sconn->expirationTime = end;
374 sconn->authenticated = 1;
377 code = tsp->user_ok (client.name, client.instance, client.cell, kvno);
378 if (code) return RXKADNOAUTH;
380 else { /* save the info for later retreival */
381 int size = sizeof(struct rxkad_serverinfo);
382 rock = (struct rxkad_serverinfo *) osi_Alloc (size);
385 bcopy (&client, &rock->client, sizeof(rock->client));
391 /* return useful authentication info about a server-side connection */
393 afs_int32 rxkad_GetServerInfo (aconn, level, expiration,
394 name, instance, cell, kvno)
395 struct rx_connection *aconn;
397 afs_uint32 *expiration;
403 struct rxkad_sconn *sconn;
405 sconn = (struct rxkad_sconn *) aconn->securityData;
406 if (sconn && sconn->authenticated && sconn->rock &&
407 (time(0) < sconn->expirationTime)) {
408 if (level) *level = sconn->level;
409 if (expiration) *expiration = sconn->expirationTime;
410 if (name) strcpy (name, sconn->rock->client.name);
411 if (instance) strcpy (instance, sconn->rock->client.instance);
412 if (cell) strcpy (cell, sconn->rock->client.cell);
413 if (kvno) *kvno = sconn->rock->kvno;
416 else return RXKADNOAUTH;