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>
21 #include <sys/types.h>
26 #include <netinet/in.h>
38 #include <afs/afsutil.h>
39 #include "private_data.h"
40 #define XPRT_RXKAD_SERVER
43 * This can be set to allow alternate ticket decoding.
44 * Currently only used by the AFS/DFS protocol translator to recognize
45 * Kerberos V5 tickets. The actual code to do that is provided externally.
47 afs_int32(*rxkad_AlternateTicketDecoder) ();
49 static struct rx_securityOps rxkad_server_ops = {
52 rxkad_PreparePacket, /* once per packet creation */
53 0, /* send packet (once per retrans) */
54 rxkad_CheckAuthentication,
55 rxkad_CreateChallenge,
59 rxkad_CheckPacket, /* check data packet */
60 rxkad_DestroyConnection,
66 extern afs_uint32 rx_MyMaxSendSize;
68 /* Miscellaneous random number routines that use the fcrypt module and the
71 static fc_KeySchedule random_int32_schedule;
73 #ifdef AFS_PTHREAD_ENV
75 * This mutex protects the following global variables:
76 * random_int32_schedule
81 pthread_mutex_t rxkad_random_mutex;
82 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0);
83 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0);
87 #endif /* AFS_PTHREAD_ENV */
90 init_random_int32(void)
94 gettimeofday(&key, NULL);
95 LOCK_RM fc_keysched(&key, random_int32_schedule);
99 get_random_int32(void)
101 static struct timeval seed;
104 LOCK_RM fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
109 /* Called with four parameters. The first is the level of encryption, as
110 defined in the rxkad.h file. The second and third are a rock and a
111 procedure that is called with the key version number that accompanies the
112 ticket and returns a pointer to the server's decryption key. The fourth
113 argument, if not NULL, is a pointer to a function that will be called for
114 every new connection with the name, instance and cell of the client. The
115 routine should return zero if the user is NOT acceptible to the server. If
116 this routine is not supplied, the server can call rxkad_GetServerInfo with
117 the rx connection pointer passed to the RPC routine to obtain information
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 *
127 struct rx_securityClass *
128 rxkad_NewServerSecurityObject(rxkad_level level, char *get_key_rock,
129 int (*get_key) (char *get_key_rock, int kvno,
130 struct ktc_encryptionKey *
132 int (*user_ok) (char *name, char *instance,
133 char *cell, afs_int32 kvno))
135 struct rx_securityClass *tsc;
136 struct rxkad_sprivate *tsp;
142 size = sizeof(struct rx_securityClass);
143 tsc = (struct rx_securityClass *)osi_Alloc(size);
144 memset(tsc, 0, size);
145 tsc->refCount = 1; /* caller has one reference */
146 tsc->ops = &rxkad_server_ops;
147 size = sizeof(struct rxkad_sprivate);
148 tsp = (struct rxkad_sprivate *)osi_Alloc(size);
149 memset(tsp, 0, size);
150 tsc->privateData = (char *)tsp;
152 tsp->type |= rxkad_server; /* so can identify later */
153 tsp->level = level; /* level of encryption */
154 tsp->get_key_rock = get_key_rock;
155 tsp->get_key = get_key; /* to get server ticket */
156 tsp->user_ok = user_ok; /* to inform server of client id. */
159 LOCK_RXKAD_STATS rxkad_stats_serverObjects++;
160 UNLOCK_RXKAD_STATS return tsc;
163 /* server: called to tell if a connection authenticated properly */
166 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
167 struct rx_connection *aconn)
169 struct rxkad_sconn *sconn;
171 /* first make sure the object exists */
172 if (!aconn->securityData)
173 return RXKADINCONSISTENCY;
175 sconn = (struct rxkad_sconn *)aconn->securityData;
176 return !sconn->authenticated;
179 /* server: put the current challenge in the connection structure for later use
183 rxkad_CreateChallenge(struct rx_securityClass *aobj,
184 struct rx_connection *aconn)
186 struct rxkad_sconn *sconn;
187 struct rxkad_sprivate *tsp;
189 sconn = (struct rxkad_sconn *)aconn->securityData;
190 sconn->challengeID = get_random_int32();
191 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
192 /* initialize level from object's minimum acceptable level */
193 tsp = (struct rxkad_sprivate *)aobj->privateData;
194 sconn->level = tsp->level;
198 /* server: fill in a challenge in the packet */
201 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
202 struct rx_packet *apacket)
204 struct rxkad_sconn *sconn;
207 struct rxkad_v2Challenge c_v2; /* version 2 */
208 struct rxkad_oldChallenge c_old; /* old style */
210 sconn = (struct rxkad_sconn *)aconn->securityData;
211 if (rx_IsUsingPktCksum(aconn))
212 sconn->cksumSeen = 1;
214 if (sconn->cksumSeen) {
215 memset(&c_v2, 0, sizeof(c_v2));
216 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
217 c_v2.challengeID = htonl(sconn->challengeID);
218 c_v2.level = htonl((afs_int32) sconn->level);
220 challenge = (char *)&c_v2;
221 challengeSize = sizeof(c_v2);
223 memset(&c_old, 0, sizeof(c_old));
224 c_old.challengeID = htonl(sconn->challengeID);
225 c_old.level = htonl((afs_int32) sconn->level);
226 challenge = (char *)&c_old;
227 challengeSize = sizeof(c_old);
229 if (rx_MyMaxSendSize < challengeSize)
230 return RXKADPACKETSHORT; /* not enough space */
232 rx_packetwrite(apacket, 0, challengeSize, challenge);
233 rx_SetDataSize(apacket, challengeSize);
235 LOCK_RXKAD_STATS rxkad_stats.challengesSent++;
236 UNLOCK_RXKAD_STATS return 0;
239 /* server: process a response to a challenge packet */
240 /* XXX this does some copying of data in and out of the packet, but I'll bet it
241 * could just do it in place, especially if I used rx_Pullup...
244 rxkad_CheckResponse(struct rx_securityClass *aobj,
245 struct rx_connection *aconn, struct rx_packet *apacket)
247 struct rxkad_sconn *sconn;
248 struct rxkad_sprivate *tsp;
249 struct ktc_encryptionKey serverKey;
250 struct rxkad_oldChallengeResponse oldr; /* response format */
251 struct rxkad_v2ChallengeResponse v2r;
252 afs_int32 tlen; /* ticket len */
253 afs_int32 kvno; /* key version of ticket */
254 char tix[MAXKTCTICKETLEN];
255 afs_int32 incChallengeID;
258 /* ticket contents */
259 struct ktc_principal client;
260 struct ktc_encryptionKey sessionkey;
265 struct rxkad_serverinfo *rock;
267 sconn = (struct rxkad_sconn *)aconn->securityData;
268 tsp = (struct rxkad_sprivate *)aobj->privateData;
270 if (sconn->cksumSeen) {
271 /* expect v2 response, leave fields in v2r in network order for cksum
272 * computation which follows decryption. */
273 if (rx_GetDataSize(apacket) < sizeof(v2r))
274 return RXKADPACKETSHORT;
275 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
279 kvno = ntohl(v2r.kvno);
280 tlen = ntohl(v2r.ticketLen);
281 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
282 return RXKADPACKETSHORT;
284 /* expect old format response */
285 if (rx_GetDataSize(apacket) < sizeof(oldr))
286 return RXKADPACKETSHORT;
287 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
290 kvno = ntohl(oldr.kvno);
291 tlen = ntohl(oldr.ticketLen);
292 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
293 return RXKADPACKETSHORT;
295 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
296 return RXKADTICKETLEN;
298 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
301 * We allow the ticket to be optionally decoded by an alternate
302 * ticket decoder, if the function variable
303 * rxkad_AlternateTicketDecoder is set. That function should
304 * return a code of -1 if it wants the ticket to be decoded by
305 * the standard decoder.
307 if (rxkad_AlternateTicketDecoder) {
309 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
310 client.instance, client.cell,
311 &sessionkey, &host, &start, &end);
312 if (code && code != -1) {
316 code = -1; /* No alternate ticket decoder present */
320 * If the alternate decoder is not present, or returns -1, then
321 * assume the ticket is of the default style.
323 if (code == -1 && (kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
324 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY)) {
326 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
327 kvno, client.name, client.instance, client.cell,
328 &sessionkey, &host, &start, &end);
334 * If the alternate decoder/kerberos 5 decoder is not present, or
335 * returns -1, then assume the ticket is of the default style.
338 /* get ticket's key */
339 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
341 return RXKADUNKNOWNKEY; /* invalid kvno */
343 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
344 client.instance, client.cell, &sessionkey, &host,
347 return RXKADBADTICKET;
349 code = tkt_CheckTimes(start, end, time(0));
355 code = fc_keysched(&sessionkey, sconn->keysched);
358 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
360 if (sconn->cksumSeen) {
361 /* using v2 response */
362 afs_uint32 cksum; /* observed cksum */
363 struct rxkad_endpoint endpoint; /* connections endpoint */
367 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
368 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
369 sconn->keysched, xor, DECRYPT);
370 cksum = rxkad_CksumChallengeResponse(&v2r);
371 if (cksum != v2r.encrypted.endpoint.cksum)
372 return RXKADSEALEDINCON;
373 (void)rxkad_SetupEndpoint(aconn, &endpoint);
374 v2r.encrypted.endpoint.cksum = 0;
375 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
376 return RXKADSEALEDINCON;
377 for (i = 0; i < RX_MAXCALLS; i++) {
378 v2r.encrypted.callNumbers[i] =
379 ntohl(v2r.encrypted.callNumbers[i]);
380 if (v2r.encrypted.callNumbers[i] < 0)
381 return RXKADSEALEDINCON;
384 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
385 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
386 level = ntohl(v2r.encrypted.level);
388 /* expect old format response */
389 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
391 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
392 level = ntohl(oldr.encrypted.level);
394 if (incChallengeID != sconn->challengeID + 1)
395 return RXKADOUTOFSEQUENCE; /* replay attempt */
396 if ((level < sconn->level) || (level > rxkad_crypt))
397 return RXKADLEVELFAIL;
398 sconn->level = level;
399 rxkad_SetLevel(aconn, sconn->level);
400 LOCK_RXKAD_STATS rxkad_stats.responses[rxkad_LevelIndex(sconn->level)]++;
402 /* now compute endpoint-specific info used for computing 16 bit checksum */
403 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec,
406 /* otherwise things are ok */
407 sconn->expirationTime = end;
408 sconn->authenticated = 1;
411 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
414 } else { /* save the info for later retreival */
415 int size = sizeof(struct rxkad_serverinfo);
416 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
417 memset(rock, 0, size);
419 memcpy(&rock->client, &client, sizeof(rock->client));
425 /* return useful authentication info about a server-side connection */
428 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
429 afs_uint32 * expiration, char *name, char *instance,
430 char *cell, afs_int32 * kvno)
432 struct rxkad_sconn *sconn;
434 sconn = (struct rxkad_sconn *)aconn->securityData;
435 if (sconn && sconn->authenticated && sconn->rock
436 && (time(0) < sconn->expirationTime)) {
438 *level = sconn->level;
440 *expiration = sconn->expirationTime;
442 strcpy(name, sconn->rock->client.name);
444 strcpy(instance, sconn->rock->client.instance);
446 strcpy(cell, sconn->rock->client.cell);
448 *kvno = sconn->rock->kvno;