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 <afs/param.h>
16 #include <sys/types.h>
21 #include <netinet/in.h>
26 #include <afs/afsutil.h>
27 #include "private_data.h"
28 #define XPRT_RXKAD_SERVER
29 #include "../permit_xprt.h"
33 * This can be set to allow alternate ticket decoding.
34 * Currently only used by the AFS/DFS protocol translator to recognize
35 * Kerberos V5 tickets. The actual code to do that is provided externally.
37 afs_int32 (*rxkad_AlternateTicketDecoder)();
39 static struct rx_securityOps rxkad_server_ops = {
42 rxkad_PreparePacket, /* once per packet creation */
43 0, /* send packet (once per retrans) */
44 rxkad_CheckAuthentication,
45 rxkad_CreateChallenge,
49 rxkad_CheckPacket, /* check data packet */
50 rxkad_DestroyConnection,
53 extern afs_uint32 rx_MyMaxSendSize;
55 /* Miscellaneous random number routines that use the fcrypt module and the
58 static fc_KeySchedule random_int32_schedule;
60 #ifdef AFS_PTHREAD_ENV
62 * This mutex protects the following global variables:
63 * random_int32_schedule
68 pthread_mutex_t rxkad_random_mutex;
69 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0);
70 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0);
74 #endif /* AFS_PTHREAD_ENV */
76 static void init_random_int32 ()
79 gettimeofday (&key, NULL);
81 fc_keysched (&key, random_int32_schedule);
85 static afs_int32 get_random_int32 ()
86 { static struct timeval seed;
90 fc_ecb_encrypt (&seed, &seed, random_int32_schedule, ENCRYPT);
96 /* Called with four parameters. The first is the level of encryption, as
97 defined in the rxkad.h file. The second and third are a rock and a
98 procedure that is called with the key version number that accompanies the
99 ticket and returns a pointer to the server's decryption key. The fourth
100 argument, if not NULL, is a pointer to a function that will be called for
101 every new connection with the name, instance and cell of the client. The
102 routine should return zero if the user is NOT acceptible to the server. If
103 this routine is not supplied, the server can call rxkad_GetServerInfo with
104 the rx connection pointer passed to the RPC routine to obtain information
107 struct rx_securityClass *
108 rxkad_NewServerSecurityObject (level, get_key_rock, get_key, user_ok)
109 rxkad_level level; /* minimum level */
110 char *get_key_rock; /* rock for get_key implementor */
111 int (*get_key)(); /* passed kvno & addr(key) to fill */
112 int (*user_ok)(); /* passed name, inst, cell => bool */
113 { struct rx_securityClass *tsc;
114 struct rxkad_sprivate *tsp;
117 if (!get_key) return 0;
119 size = sizeof(struct rx_securityClass);
120 tsc = (struct rx_securityClass *) osi_Alloc (size);
122 tsc->refCount = 1; /* caller has one reference */
123 tsc->ops = &rxkad_server_ops;
124 size = sizeof(struct rxkad_sprivate);
125 tsp = (struct rxkad_sprivate *) osi_Alloc (size);
127 tsc->privateData = (char *) tsp;
129 tsp->type |= rxkad_server; /* so can identify later */
130 tsp->level = level; /* level of encryption */
131 tsp->get_key_rock = get_key_rock;
132 tsp->get_key = get_key; /* to get server ticket */
133 tsp->user_ok = user_ok; /* to inform server of client id. */
134 init_random_int32 ();
137 rxkad_stats_serverObjects++;
142 /* server: called to tell if a connection authenticated properly */
144 rxs_return_t rxkad_CheckAuthentication (aobj, aconn)
145 struct rx_securityClass *aobj;
146 struct rx_connection *aconn;
147 { struct rxkad_sconn *sconn;
149 /* first make sure the object exists */
150 if (!aconn->securityData) return RXKADINCONSISTENCY;
152 sconn = (struct rxkad_sconn *) aconn->securityData;
153 return !sconn->authenticated;
156 /* server: put the current challenge in the connection structure for later use
159 rxs_return_t rxkad_CreateChallenge(aobj, aconn)
160 struct rx_securityClass *aobj;
161 struct rx_connection *aconn;
162 { struct rxkad_sconn *sconn;
163 struct rxkad_sprivate *tsp;
165 sconn = (struct rxkad_sconn *) aconn->securityData;
166 sconn->challengeID = get_random_int32 ();
167 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
168 /* initialize level from object's minimum acceptable level */
169 tsp = (struct rxkad_sprivate *)aobj->privateData;
170 sconn->level = tsp->level;
174 /* server: fill in a challenge in the packet */
176 rxs_return_t rxkad_GetChallenge (aobj, aconn, apacket)
177 IN struct rx_securityClass *aobj;
178 IN struct rx_packet *apacket;
179 IN struct rx_connection *aconn;
180 { struct rxkad_sconn *sconn;
184 struct rxkad_v2Challenge c_v2; /* version 2 */
185 struct rxkad_oldChallenge c_old; /* old style */
187 sconn = (struct rxkad_sconn *) aconn->securityData;
188 if (rx_IsUsingPktCksum(aconn)) sconn->cksumSeen = 1;
190 if (sconn->cksumSeen) {
191 bzero (&c_v2, sizeof(c_v2));
192 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
193 c_v2.challengeID = htonl(sconn->challengeID);
194 c_v2.level = htonl((afs_int32)sconn->level);
196 challenge = (char *)&c_v2;
197 challengeSize = sizeof(c_v2);
199 bzero (&c_old, sizeof(c_old));
200 c_old.challengeID = htonl(sconn->challengeID);
201 c_old.level = htonl((afs_int32)sconn->level);
202 challenge = (char *)&c_old;
203 challengeSize = sizeof(c_old);
205 if (rx_MyMaxSendSize < challengeSize)
206 return RXKADPACKETSHORT; /* not enough space */
208 rx_packetwrite(apacket, 0, challengeSize, challenge);
209 rx_SetDataSize (apacket, challengeSize);
212 rxkad_stats.challengesSent++;
217 /* server: process a response to a challenge packet */
218 /* XXX this does some copying of data in and out of the packet, but I'll bet it
219 * could just do it in place, especially if I used rx_Pullup...
221 rxs_return_t rxkad_CheckResponse (aobj, aconn, apacket)
222 struct rx_securityClass *aobj;
223 struct rx_packet *apacket;
224 struct rx_connection *aconn;
225 { struct rxkad_sconn *sconn;
226 struct rxkad_sprivate *tsp;
227 struct ktc_encryptionKey serverKey;
228 struct rxkad_oldChallengeResponse oldr; /* response format */
229 struct rxkad_v2ChallengeResponse v2r;
230 afs_int32 tlen; /* ticket len */
231 afs_int32 kvno; /* key version of ticket */
232 char tix[MAXKTCTICKETLEN];
233 afs_int32 incChallengeID;
236 /* ticket contents */
237 struct ktc_principal client;
238 struct ktc_encryptionKey sessionkey;
243 struct rxkad_serverinfo *rock;
245 sconn = (struct rxkad_sconn *) aconn->securityData;
246 tsp = (struct rxkad_sprivate *) aobj->privateData;
248 if (sconn->cksumSeen) {
249 /* expect v2 response, leave fields in v2r in network order for cksum
250 * computation which follows decryption. */
251 if (rx_GetDataSize(apacket) < sizeof(v2r))
252 return RXKADPACKETSHORT;
253 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
257 kvno = ntohl (v2r.kvno);
258 tlen = ntohl(v2r.ticketLen);
259 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
260 return RXKADPACKETSHORT;
262 /* expect old format response */
263 if (rx_GetDataSize(apacket) < sizeof(oldr)) return RXKADPACKETSHORT;
264 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
267 kvno = ntohl (oldr.kvno);
268 tlen = ntohl(oldr.ticketLen);
269 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
270 return RXKADPACKETSHORT;
272 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
273 return RXKADTICKETLEN;
275 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
278 * We allow the ticket to be optionally decoded by an alternate
279 * ticket decoder, if the function variable
280 * rxkad_AlternateTicketDecoder is set. That function should
281 * return a code of -1 if it wants the ticket to be decoded by
282 * the standard decoder.
284 if (rxkad_AlternateTicketDecoder) {
285 code = rxkad_AlternateTicketDecoder
286 (kvno, tix, tlen, client.name, client.instance, client.cell,
287 &sessionkey, &host, &start, &end);
288 if (code && code != -1) {
292 code = -1; /* No alternate ticket decoder present */
296 * If the alternate decoder is not present, or returns -1, then
297 * assume the ticket is of the default style.
300 /* get ticket's key */
301 code = (*tsp->get_key)(tsp->get_key_rock, kvno, &serverKey);
302 if (code) return RXKADUNKNOWNKEY; /* invalid kvno */
303 code = tkt_DecodeTicket (tix, tlen, &serverKey,
304 client.name, client.instance, client.cell,
305 &sessionkey, &host, &start, &end);
306 if (code) return RXKADBADTICKET;
308 code = tkt_CheckTimes (start, end, time(0));
309 if (code == -1) return RXKADEXPIRED;
310 else if (code <= 0) return RXKADNOAUTH;
312 code = fc_keysched (&sessionkey, sconn->keysched);
313 if (code) return RXKADBADKEY;
314 bcopy (&sessionkey, sconn->ivec, sizeof(sconn->ivec));
316 if (sconn->cksumSeen) {
317 /* using v2 response */
318 afs_uint32 cksum; /* observed cksum */
319 struct rxkad_endpoint endpoint; /* connections endpoint */
323 bcopy(sconn->ivec, xor, 2*sizeof(afs_int32));
324 fc_cbc_encrypt (&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
325 sconn->keysched, xor, DECRYPT);
326 cksum = rxkad_CksumChallengeResponse (&v2r);
327 if (cksum != v2r.encrypted.endpoint.cksum)
328 return RXKADSEALEDINCON;
329 (void) rxkad_SetupEndpoint (aconn, &endpoint);
330 v2r.encrypted.endpoint.cksum = 0;
331 if (bcmp (&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
332 return RXKADSEALEDINCON;
333 for (i=0; i<RX_MAXCALLS; i++) {
334 v2r.encrypted.callNumbers[i] = ntohl(v2r.encrypted.callNumbers[i]);
335 if (v2r.encrypted.callNumbers[i] < 0) return RXKADSEALEDINCON;
338 (void) rxi_SetCallNumberVector (aconn, v2r.encrypted.callNumbers);
339 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
340 level = ntohl(v2r.encrypted.level);
342 /* expect old format response */
343 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted,
344 sconn->keysched, DECRYPT);
345 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
346 level = ntohl(oldr.encrypted.level);
348 if (incChallengeID != sconn->challengeID+1)
349 return RXKADOUTOFSEQUENCE; /* replay attempt */
350 if ((level < sconn->level) || (level > rxkad_crypt)) return RXKADLEVELFAIL;
351 sconn->level = level;
352 rxkad_SetLevel (aconn, sconn->level);
354 rxkad_stats.responses[rxkad_LevelIndex(sconn->level)]++;
357 /* now compute endpoint-specific info used for computing 16 bit checksum */
358 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
360 /* otherwise things are ok */
361 sconn->expirationTime = end;
362 sconn->authenticated = 1;
365 code = tsp->user_ok (client.name, client.instance, client.cell, kvno);
366 if (code) return RXKADNOAUTH;
368 else { /* save the info for later retreival */
369 int size = sizeof(struct rxkad_serverinfo);
370 rock = (struct rxkad_serverinfo *) osi_Alloc (size);
373 bcopy (&client, &rock->client, sizeof(rock->client));
379 /* return useful authentication info about a server-side connection */
381 afs_int32 rxkad_GetServerInfo (aconn, level, expiration,
382 name, instance, cell, kvno)
383 struct rx_connection *aconn;
385 afs_uint32 *expiration;
391 struct rxkad_sconn *sconn;
393 sconn = (struct rxkad_sconn *) aconn->securityData;
394 if (sconn && sconn->authenticated && sconn->rock &&
395 (time(0) < sconn->expirationTime)) {
396 if (level) *level = sconn->level;
397 if (expiration) *expiration = sconn->expirationTime;
398 if (name) strcpy (name, sconn->rock->client.name);
399 if (instance) strcpy (instance, sconn->rock->client.instance);
400 if (cell) strcpy (cell, sconn->rock->client.cell);
401 if (kvno) *kvno = sconn->rock->kvno;
404 else return RXKADNOAUTH;