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 #if (defined(AFS_AIX_ENV) && defined(KERNEL) && !defined(UKERNEL)) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
21 #include <sys/systm.h>
26 #include <rx/rx_packet.h>
27 #include <afs/afsutil.h>
30 #include "private_data.h"
31 #define XPRT_RXKAD_SERVER
34 * This can be set to allow alternate ticket decoding.
35 * Currently only used by the AFS/DFS protocol translator to recognize
36 * Kerberos V5 tickets. The actual code to do that is provided externally.
38 afs_int32(*rxkad_AlternateTicketDecoder) (afs_int32, char *, afs_int32,
39 char *, char *, char *,
40 struct ktc_encryptionKey *,
41 afs_int32 *, afs_uint32 *,
44 static struct rx_securityOps rxkad_server_ops = {
47 rxkad_PreparePacket, /* once per packet creation */
48 0, /* send packet (once per retrans) */
49 rxkad_CheckAuthentication,
50 rxkad_CreateChallenge,
54 rxkad_CheckPacket, /* check data packet */
55 rxkad_DestroyConnection,
57 rxkad_SetConfiguration,
61 extern afs_uint32 rx_MyMaxSendSize;
63 /* Miscellaneous random number routines that use the fcrypt module and the
66 static fc_KeySchedule random_int32_schedule;
68 #ifdef AFS_PTHREAD_ENV
70 * This mutex protects the following global variables:
71 * random_int32_schedule
75 pthread_mutex_t rxkad_random_mutex
76 #ifdef PTHREAD_MUTEX_INITIALIZER
77 = PTHREAD_MUTEX_INITIALIZER
80 #define LOCK_RM osi_Assert(pthread_mutex_lock(&rxkad_random_mutex)==0)
81 #define UNLOCK_RM osi_Assert(pthread_mutex_unlock(&rxkad_random_mutex)==0)
85 #endif /* AFS_PTHREAD_ENV */
88 init_random_int32(void)
92 gettimeofday(&key, NULL);
94 fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
99 get_random_int32(void)
101 static struct timeval seed;
105 fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
111 /* Called with four parameters. The first is the level of encryption, as
112 defined in the rxkad.h file. The second and third are a rock and a
113 procedure that is called with the key version number that accompanies the
114 ticket and returns a pointer to the server's decryption key. The fourth
115 argument, if not NULL, is a pointer to a function that will be called for
116 every new connection with the name, instance and cell of the client. The
117 routine should return zero if the user is NOT acceptible to the server. If
118 this routine is not supplied, the server can call rxkad_GetServerInfo with
119 the rx connection pointer passed to the RPC routine to obtain information
123 rxkad_level level; * minimum level *
124 char *get_key_rock; * rock for get_key implementor *
125 int (*get_key)(); * passed kvno & addr(key) to fill *
126 int (*user_ok)(); * passed name, inst, cell => bool *
129 struct rx_securityClass *
130 rxkad_NewServerSecurityObject(rxkad_level level, void *get_key_rock,
131 int (*get_key) (void *get_key_rock, int kvno,
132 struct ktc_encryptionKey *
134 int (*user_ok) (char *name, char *instance,
135 char *cell, afs_int32 kvno))
137 struct rx_securityClass *tsc;
138 struct rxkad_sprivate *tsp;
146 size = sizeof(struct rx_securityClass);
147 tsc = (struct rx_securityClass *)osi_Alloc(size);
148 memset(tsc, 0, size);
149 tsc->refCount = 1; /* caller has one reference */
150 tsc->ops = &rxkad_server_ops;
151 size = sizeof(struct rxkad_sprivate);
152 tsp = (struct rxkad_sprivate *)osi_Alloc(size);
153 memset(tsp, 0, size);
154 tsc->privateData = (char *)tsp;
156 tsp->type |= rxkad_server; /* so can identify later */
157 tsp->level = level; /* level of encryption */
158 tsp->get_key_rock = get_key_rock;
159 tsp->get_key = get_key; /* to get server ticket */
160 tsp->user_ok = user_ok; /* to inform server of client id. */
163 INC_RXKAD_STATS(serverObjects);
167 /* server: called to tell if a connection authenticated properly */
170 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
171 struct rx_connection *aconn)
173 struct rxkad_sconn *sconn = rx_GetSecurityData(aconn);
175 /* first make sure the object exists */
177 return RXKADINCONSISTENCY;
179 return !sconn->authenticated;
182 /* server: put the current challenge in the connection structure for later use
186 rxkad_CreateChallenge(struct rx_securityClass *aobj,
187 struct rx_connection *aconn)
189 struct rxkad_sconn *sconn = rx_GetSecurityData(aconn);
190 struct rxkad_sprivate *tsp;
192 sconn->challengeID = get_random_int32();
193 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
194 /* initialize level from object's minimum acceptable level */
195 tsp = (struct rxkad_sprivate *)aobj->privateData;
196 sconn->level = tsp->level;
200 /* server: fill in a challenge in the packet */
203 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
204 struct rx_packet *apacket)
206 struct rxkad_sconn *sconn = rx_GetSecurityData(aconn);
209 struct rxkad_v2Challenge c_v2; /* version 2 */
210 struct rxkad_oldChallenge c_old; /* old style */
212 if (rx_IsUsingPktCksum(aconn))
213 sconn->cksumSeen = 1;
215 if (sconn->cksumSeen) {
216 memset(&c_v2, 0, sizeof(c_v2));
217 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
218 c_v2.challengeID = htonl(sconn->challengeID);
219 c_v2.level = htonl((afs_int32) sconn->level);
221 challenge = (char *)&c_v2;
222 challengeSize = sizeof(c_v2);
224 memset(&c_old, 0, sizeof(c_old));
225 c_old.challengeID = htonl(sconn->challengeID);
226 c_old.level = htonl((afs_int32) sconn->level);
227 challenge = (char *)&c_old;
228 challengeSize = sizeof(c_old);
230 if (rx_MyMaxSendSize < challengeSize)
231 return RXKADPACKETSHORT; /* not enough space */
233 rx_packetwrite(apacket, 0, challengeSize, challenge);
234 rx_SetDataSize(apacket, challengeSize);
236 INC_RXKAD_STATS(challengesSent);
240 /* server: process a response to a challenge packet */
241 /* XXX this does some copying of data in and out of the packet, but I'll bet it
242 * could just do it in place, especially if I used rx_Pullup...
245 rxkad_CheckResponse(struct rx_securityClass *aobj,
246 struct rx_connection *aconn, struct rx_packet *apacket)
248 struct rxkad_sconn *sconn;
249 struct rxkad_sprivate *tsp;
250 struct ktc_encryptionKey serverKey;
251 struct rxkad_oldChallengeResponse oldr; /* response format */
252 struct rxkad_v2ChallengeResponse v2r;
253 afs_int32 tlen; /* ticket len */
254 afs_int32 kvno; /* key version of ticket */
255 char tix[MAXKTCTICKETLEN];
256 afs_int32 incChallengeID;
259 /* ticket contents */
260 struct ktc_principal client;
261 struct ktc_encryptionKey sessionkey;
266 struct rxkad_serverinfo *rock;
268 sconn = rx_GetSecurityData(aconn);
269 tsp = (struct rxkad_sprivate *)aobj->privateData;
271 if (sconn->cksumSeen) {
272 /* expect v2 response, leave fields in v2r in network order for cksum
273 * computation which follows decryption. */
274 if (rx_GetDataSize(apacket) < sizeof(v2r))
275 return RXKADPACKETSHORT;
276 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
280 kvno = ntohl(v2r.kvno);
281 tlen = ntohl(v2r.ticketLen);
282 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
283 return RXKADPACKETSHORT;
285 /* expect old format response */
286 if (rx_GetDataSize(apacket) < sizeof(oldr))
287 return RXKADPACKETSHORT;
288 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
291 kvno = ntohl(oldr.kvno);
292 tlen = ntohl(oldr.ticketLen);
293 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
294 return RXKADPACKETSHORT;
296 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
297 return RXKADTICKETLEN;
299 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
302 * We allow the ticket to be optionally decoded by an alternate
303 * ticket decoder, if the function variable
304 * rxkad_AlternateTicketDecoder is set. That function should
305 * return a code of -1 if it wants the ticket to be decoded by
306 * the standard decoder.
308 if (rxkad_AlternateTicketDecoder) {
310 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
311 client.instance, client.cell,
312 &sessionkey, &host, &start, &end);
313 if (code && code != -1) {
317 code = -1; /* No alternate ticket decoder present */
321 * If the alternate decoder is not present, or returns -1, then
322 * assume the ticket is of the default style.
324 if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
325 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
327 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
328 kvno, client.name, client.instance, client.cell,
329 &sessionkey, &host, &start, &end,
330 tsp->flags & RXS_CONFIG_FLAGS_DISABLE_DOTCHECK);
336 * If the alternate decoder/kerberos 5 decoder is not present, or
337 * returns -1, then assume the ticket is of the default style.
340 /* get ticket's key */
341 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
343 return RXKADUNKNOWNKEY; /* invalid kvno */
345 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
346 client.instance, client.cell, &sessionkey, &host,
351 code = tkt_CheckTimes(start, end, time(0));
357 return RXKADBADTICKET;
359 code = fc_keysched(&sessionkey, sconn->keysched);
362 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
364 if (sconn->cksumSeen) {
365 /* using v2 response */
366 afs_uint32 cksum; /* observed cksum */
367 struct rxkad_endpoint endpoint; /* connections endpoint */
371 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
372 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
373 sconn->keysched, xor, DECRYPT);
374 cksum = rxkad_CksumChallengeResponse(&v2r);
375 if (cksum != v2r.encrypted.endpoint.cksum)
376 return RXKADSEALEDINCON;
377 (void)rxkad_SetupEndpoint(aconn, &endpoint);
378 v2r.encrypted.endpoint.cksum = 0;
379 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
380 return RXKADSEALEDINCON;
381 for (i = 0; i < RX_MAXCALLS; i++) {
382 v2r.encrypted.callNumbers[i] =
383 ntohl(v2r.encrypted.callNumbers[i]);
384 if (v2r.encrypted.callNumbers[i] < 0)
385 return RXKADSEALEDINCON;
388 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
389 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
390 level = ntohl(v2r.encrypted.level);
392 /* expect old format response */
393 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
395 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
396 level = ntohl(oldr.encrypted.level);
398 if (incChallengeID != sconn->challengeID + 1)
399 return RXKADOUTOFSEQUENCE; /* replay attempt */
400 if ((level < sconn->level) || (level > rxkad_crypt))
401 return RXKADLEVELFAIL;
402 sconn->level = level;
403 rxkad_SetLevel(aconn, sconn->level);
404 INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
405 /* now compute endpoint-specific info used for computing 16 bit checksum */
406 rxkad_DeriveXORInfo(aconn, &sconn->keysched, (char *)sconn->ivec, (char *)sconn->preSeq);
408 /* otherwise things are ok */
409 sconn->expirationTime = end;
410 sconn->authenticated = 1;
413 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
416 } else { /* save the info for later retreival */
417 int size = sizeof(struct rxkad_serverinfo);
418 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
419 memset(rock, 0, size);
421 memcpy(&rock->client, &client, sizeof(rock->client));
427 /* return useful authentication info about a server-side connection */
430 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
431 afs_uint32 * expiration, char *name, char *instance,
432 char *cell, afs_int32 * kvno)
434 struct rxkad_sconn *sconn;
436 sconn = rx_GetSecurityData(aconn);
437 if (sconn && sconn->authenticated && sconn->rock
438 && (time(0) < sconn->expirationTime)) {
440 *level = sconn->level;
442 *expiration = sconn->expirationTime;
444 strcpy(name, sconn->rock->client.name);
446 strcpy(instance, sconn->rock->client.instance);
448 strcpy(cell, sconn->rock->client.cell);
450 *kvno = sconn->rock->kvno;
456 /* Set security object configuration variables */
457 afs_int32 rxkad_SetConfiguration(struct rx_securityClass *aobj,
458 struct rx_connection *aconn,
459 rx_securityConfigVariables atype,
460 void * avalue, void **currentValue)
462 struct rxkad_sprivate *private =
463 (struct rxkad_sprivate *) aobj->privateData;
466 case RXS_CONFIG_FLAGS:
468 *((afs_uint32 *)currentValue) = private->flags;
470 private->flags = (intptr_t)avalue;