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>
19 #include <sys/types.h>
20 #if (defined(AFS_AIX_ENV) && defined(KERNEL) && !defined(UKERNEL)) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
21 #include <sys/systm.h>
27 #include <netinet/in.h>
33 #include <afs/afsutil.h>
34 #include <des/stats.h>
35 #include "private_data.h"
36 #define XPRT_RXKAD_SERVER
39 * This can be set to allow alternate ticket decoding.
40 * Currently only used by the AFS/DFS protocol translator to recognize
41 * Kerberos V5 tickets. The actual code to do that is provided externally.
43 afs_int32(*rxkad_AlternateTicketDecoder) (afs_int32, char *, afs_int32,
44 char *, char *, char *,
45 struct ktc_encryptionKey *,
46 afs_int32 *, afs_uint32 *,
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,
62 rxkad_SetConfiguration,
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);
96 fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
101 get_random_int32(void)
103 static struct timeval seed;
107 fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
113 /* Called with four parameters. The first is the level of encryption, as
114 defined in the rxkad.h file. The second and third are a rock and a
115 procedure that is called with the key version number that accompanies the
116 ticket and returns a pointer to the server's decryption key. The fourth
117 argument, if not NULL, is a pointer to a function that will be called for
118 every new connection with the name, instance and cell of the client. The
119 routine should return zero if the user is NOT acceptible to the server. If
120 this routine is not supplied, the server can call rxkad_GetServerInfo with
121 the rx connection pointer passed to the RPC routine to obtain information
125 rxkad_level level; * minimum level *
126 char *get_key_rock; * rock for get_key implementor *
127 int (*get_key)(); * passed kvno & addr(key) to fill *
128 int (*user_ok)(); * passed name, inst, cell => bool *
131 struct rx_securityClass *
132 rxkad_NewServerSecurityObject(rxkad_level level, void *get_key_rock,
133 int (*get_key) (void *get_key_rock, int kvno,
134 struct ktc_encryptionKey *
136 int (*user_ok) (char *name, char *instance,
137 char *cell, afs_int32 kvno))
139 struct rx_securityClass *tsc;
140 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;
175 /* first make sure the object exists */
176 if (!aconn->securityData)
177 return RXKADINCONSISTENCY;
179 sconn = (struct rxkad_sconn *)aconn->securityData;
180 return !sconn->authenticated;
183 /* server: put the current challenge in the connection structure for later use
187 rxkad_CreateChallenge(struct rx_securityClass *aobj,
188 struct rx_connection *aconn)
190 struct rxkad_sconn *sconn;
191 struct rxkad_sprivate *tsp;
193 sconn = (struct rxkad_sconn *)aconn->securityData;
194 sconn->challengeID = get_random_int32();
195 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
196 /* initialize level from object's minimum acceptable level */
197 tsp = (struct rxkad_sprivate *)aobj->privateData;
198 sconn->level = tsp->level;
202 /* server: fill in a challenge in the packet */
205 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
206 struct rx_packet *apacket)
208 struct rxkad_sconn *sconn;
211 struct rxkad_v2Challenge c_v2; /* version 2 */
212 struct rxkad_oldChallenge c_old; /* old style */
214 sconn = (struct rxkad_sconn *)aconn->securityData;
215 if (rx_IsUsingPktCksum(aconn))
216 sconn->cksumSeen = 1;
218 if (sconn->cksumSeen) {
219 memset(&c_v2, 0, sizeof(c_v2));
220 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
221 c_v2.challengeID = htonl(sconn->challengeID);
222 c_v2.level = htonl((afs_int32) sconn->level);
224 challenge = (char *)&c_v2;
225 challengeSize = sizeof(c_v2);
227 memset(&c_old, 0, sizeof(c_old));
228 c_old.challengeID = htonl(sconn->challengeID);
229 c_old.level = htonl((afs_int32) sconn->level);
230 challenge = (char *)&c_old;
231 challengeSize = sizeof(c_old);
233 if (rx_MyMaxSendSize < challengeSize)
234 return RXKADPACKETSHORT; /* not enough space */
236 rx_packetwrite(apacket, 0, challengeSize, challenge);
237 rx_SetDataSize(apacket, challengeSize);
239 INC_RXKAD_STATS(challengesSent);
243 /* server: process a response to a challenge packet */
244 /* XXX this does some copying of data in and out of the packet, but I'll bet it
245 * could just do it in place, especially if I used rx_Pullup...
248 rxkad_CheckResponse(struct rx_securityClass *aobj,
249 struct rx_connection *aconn, struct rx_packet *apacket)
251 struct rxkad_sconn *sconn;
252 struct rxkad_sprivate *tsp;
253 struct ktc_encryptionKey serverKey;
254 struct rxkad_oldChallengeResponse oldr; /* response format */
255 struct rxkad_v2ChallengeResponse v2r;
256 afs_int32 tlen; /* ticket len */
257 afs_int32 kvno; /* key version of ticket */
258 char tix[MAXKTCTICKETLEN];
259 afs_int32 incChallengeID;
262 /* ticket contents */
263 struct ktc_principal client;
264 struct ktc_encryptionKey sessionkey;
269 struct rxkad_serverinfo *rock;
271 sconn = (struct rxkad_sconn *)aconn->securityData;
272 tsp = (struct rxkad_sprivate *)aobj->privateData;
274 if (sconn->cksumSeen) {
275 /* expect v2 response, leave fields in v2r in network order for cksum
276 * computation which follows decryption. */
277 if (rx_GetDataSize(apacket) < sizeof(v2r))
278 return RXKADPACKETSHORT;
279 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
283 kvno = ntohl(v2r.kvno);
284 tlen = ntohl(v2r.ticketLen);
285 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
286 return RXKADPACKETSHORT;
288 /* expect old format response */
289 if (rx_GetDataSize(apacket) < sizeof(oldr))
290 return RXKADPACKETSHORT;
291 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
294 kvno = ntohl(oldr.kvno);
295 tlen = ntohl(oldr.ticketLen);
296 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
297 return RXKADPACKETSHORT;
299 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
300 return RXKADTICKETLEN;
302 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
305 * We allow the ticket to be optionally decoded by an alternate
306 * ticket decoder, if the function variable
307 * rxkad_AlternateTicketDecoder is set. That function should
308 * return a code of -1 if it wants the ticket to be decoded by
309 * the standard decoder.
311 if (rxkad_AlternateTicketDecoder) {
313 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
314 client.instance, client.cell,
315 &sessionkey, &host, &start, &end);
316 if (code && code != -1) {
320 code = -1; /* No alternate ticket decoder present */
324 * If the alternate decoder is not present, or returns -1, then
325 * assume the ticket is of the default style.
327 if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
328 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
330 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
331 kvno, client.name, client.instance, client.cell,
332 &sessionkey, &host, &start, &end,
333 tsp->flags & RXS_CONFIG_FLAGS_DISABLE_DOTCHECK);
339 * If the alternate decoder/kerberos 5 decoder is not present, or
340 * returns -1, then assume the ticket is of the default style.
343 /* get ticket's key */
344 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
346 return RXKADUNKNOWNKEY; /* invalid kvno */
348 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
349 client.instance, client.cell, &sessionkey, &host,
354 code = tkt_CheckTimes(start, end, time(0));
360 return RXKADBADTICKET;
362 code = fc_keysched(&sessionkey, sconn->keysched);
365 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
367 if (sconn->cksumSeen) {
368 /* using v2 response */
369 afs_uint32 cksum; /* observed cksum */
370 struct rxkad_endpoint endpoint; /* connections endpoint */
374 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
375 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
376 sconn->keysched, xor, DECRYPT);
377 cksum = rxkad_CksumChallengeResponse(&v2r);
378 if (cksum != v2r.encrypted.endpoint.cksum)
379 return RXKADSEALEDINCON;
380 (void)rxkad_SetupEndpoint(aconn, &endpoint);
381 v2r.encrypted.endpoint.cksum = 0;
382 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
383 return RXKADSEALEDINCON;
384 for (i = 0; i < RX_MAXCALLS; i++) {
385 v2r.encrypted.callNumbers[i] =
386 ntohl(v2r.encrypted.callNumbers[i]);
387 if (v2r.encrypted.callNumbers[i] < 0)
388 return RXKADSEALEDINCON;
391 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
392 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
393 level = ntohl(v2r.encrypted.level);
395 /* expect old format response */
396 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
398 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
399 level = ntohl(oldr.encrypted.level);
401 if (incChallengeID != sconn->challengeID + 1)
402 return RXKADOUTOFSEQUENCE; /* replay attempt */
403 if ((level < sconn->level) || (level > rxkad_crypt))
404 return RXKADLEVELFAIL;
405 sconn->level = level;
406 rxkad_SetLevel(aconn, sconn->level);
407 INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
408 /* now compute endpoint-specific info used for computing 16 bit checksum */
409 rxkad_DeriveXORInfo(aconn, &sconn->keysched, (char *)sconn->ivec, (char *)sconn->preSeq);
411 /* otherwise things are ok */
412 sconn->expirationTime = end;
413 sconn->authenticated = 1;
416 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
419 } else { /* save the info for later retreival */
420 int size = sizeof(struct rxkad_serverinfo);
421 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
422 memset(rock, 0, size);
424 memcpy(&rock->client, &client, sizeof(rock->client));
430 /* return useful authentication info about a server-side connection */
433 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
434 afs_uint32 * expiration, char *name, char *instance,
435 char *cell, afs_int32 * kvno)
437 struct rxkad_sconn *sconn;
439 sconn = (struct rxkad_sconn *)aconn->securityData;
440 if (sconn && sconn->authenticated && sconn->rock
441 && (time(0) < sconn->expirationTime)) {
443 *level = sconn->level;
445 *expiration = sconn->expirationTime;
447 strcpy(name, sconn->rock->client.name);
449 strcpy(instance, sconn->rock->client.instance);
451 strcpy(cell, sconn->rock->client.cell);
453 *kvno = sconn->rock->kvno;
459 /* Set security object configuration variables */
460 afs_int32 rxkad_SetConfiguration(struct rx_securityClass *aobj,
461 struct rx_connection *aconn,
462 rx_securityConfigVariables atype,
463 void * avalue, void **currentValue)
465 struct rxkad_sprivate *private =
466 (struct rxkad_sprivate *) aobj->privateData;
469 case RXS_CONFIG_FLAGS:
471 *((afs_uint32 *)currentValue) = private->flags;
473 private->flags = (intptr_t)avalue;