struct rx_securityClass * rxkad_NewServerSecurityObject(rxkad_level level, void *get_key_rock, int (*get_key) (void *get_key_rock, int kvno, struct ktc_encryptionKey * serverKey), int (*user_ok) (char *name, char *instance, char *cell, afs_int32 kvno)) { struct rx_securityClass *tsc; struct rxkad_sprivate *tsp; int size; if (!get_key) return 0; size = sizeof(struct rx_securityClass); tsc = (struct rx_securityClass *)osi_Alloc(size); memset(tsc, 0, size); tsc->refCount = 1; /* caller has one reference */ tsc->ops = &rxkad_server_ops; size = sizeof(struct rxkad_sprivate); tsp = (struct rxkad_sprivate *)osi_Alloc(size); memset(tsp, 0, size); tsc->privateData = (char *)tsp; tsp->type |= rxkad_server; /* so can identify later */ tsp->level = level; /* level of encryption */ tsp->get_key_rock = get_key_rock; tsp->get_key = get_key; /* to get server ticket */ tsp->user_ok = user_ok; /* to inform server of client id. */ init_random_int32(); INC_RXKAD_STATS(serverObjects); return tsc; }
struct rx_securityClass * rxkad_NewClientSecurityObject(rxkad_level level, struct ktc_encryptionKey *sessionkey, afs_int32 kvno, int ticketLen, char *ticket) { struct rx_securityClass *tsc; struct rxkad_cprivate *tcp; int code; int size, psize; rxkad_Init(); size = sizeof(struct rx_securityClass); tsc = rxi_Alloc(size); memset((void *)tsc, 0, size); tsc->refCount = 1; /* caller gets one for free */ tsc->ops = &rxkad_client_ops; psize = PDATA_SIZE(ticketLen); tcp = rxi_Alloc(psize); memset((void *)tcp, 0, psize); tsc->privateData = (char *)tcp; tcp->type |= rxkad_client; tcp->level = level; code = fc_keysched(sessionkey, tcp->keysched); if (code) { rxi_Free(tcp, psize); rxi_Free(tsc, sizeof(struct rx_securityClass)); return 0; /* bad key */ } memcpy((void *)tcp->ivec, (void *)sessionkey, sizeof(tcp->ivec)); tcp->kvno = kvno; /* key version number */ tcp->ticketLen = ticketLen; /* length of ticket */ if (tcp->ticketLen > MAXKTCTICKETLEN) { rxi_Free(tcp, psize); rxi_Free(tsc, sizeof(struct rx_securityClass)); return 0; /* bad key */ } memcpy(tcp->ticket, ticket, ticketLen); INC_RXKAD_STATS(clientObjects); return tsc; }
int rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn, struct rx_packet *apacket) { struct rxkad_sconn *sconn; char *challenge; int challengeSize; struct rxkad_v2Challenge c_v2; /* version 2 */ struct rxkad_oldChallenge c_old; /* old style */ sconn = (struct rxkad_sconn *)aconn->securityData; if (rx_IsUsingPktCksum(aconn)) sconn->cksumSeen = 1; if (sconn->cksumSeen) { memset(&c_v2, 0, sizeof(c_v2)); c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION); c_v2.challengeID = htonl(sconn->challengeID); c_v2.level = htonl((afs_int32) sconn->level); c_v2.spare = 0; challenge = (char *)&c_v2; challengeSize = sizeof(c_v2); } else { memset(&c_old, 0, sizeof(c_old)); c_old.challengeID = htonl(sconn->challengeID); c_old.level = htonl((afs_int32) sconn->level); challenge = (char *)&c_old; challengeSize = sizeof(c_old); } if (rx_MyMaxSendSize < challengeSize) return RXKADPACKETSHORT; /* not enough space */ rx_packetwrite(apacket, 0, challengeSize, challenge); rx_SetDataSize(apacket, challengeSize); sconn->tried = 1; INC_RXKAD_STATS(challengesSent); return 0; }
/* XXX this does some copying of data in and out of the packet, but I'll bet it * could just do it in place, especially if I used rx_Pullup... */ int rxkad_CheckResponse(struct rx_securityClass *aobj, struct rx_connection *aconn, struct rx_packet *apacket) { struct rxkad_sconn *sconn; struct rxkad_sprivate *tsp; struct ktc_encryptionKey serverKey; struct rxkad_oldChallengeResponse oldr; /* response format */ struct rxkad_v2ChallengeResponse v2r; afs_int32 tlen; /* ticket len */ afs_int32 kvno; /* key version of ticket */ char tix[MAXKTCTICKETLEN]; afs_int32 incChallengeID; rxkad_level level; int code; /* ticket contents */ struct ktc_principal client; struct ktc_encryptionKey sessionkey; afs_int32 host; afs_uint32 start; afs_uint32 end; unsigned int pos; struct rxkad_serverinfo *rock; sconn = (struct rxkad_sconn *)aconn->securityData; tsp = (struct rxkad_sprivate *)aobj->privateData; if (sconn->cksumSeen) { /* expect v2 response, leave fields in v2r in network order for cksum * computation which follows decryption. */ if (rx_GetDataSize(apacket) < sizeof(v2r)) return RXKADPACKETSHORT; rx_packetread(apacket, 0, sizeof(v2r), &v2r); pos = sizeof(v2r); /* version == 2 */ /* ignore spare */ kvno = ntohl(v2r.kvno); tlen = ntohl(v2r.ticketLen); if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen) return RXKADPACKETSHORT; } else { /* expect old format response */ if (rx_GetDataSize(apacket) < sizeof(oldr)) return RXKADPACKETSHORT; rx_packetread(apacket, 0, sizeof(oldr), &oldr); pos = sizeof(oldr); kvno = ntohl(oldr.kvno); tlen = ntohl(oldr.ticketLen); if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen) return RXKADPACKETSHORT; } if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN)) return RXKADTICKETLEN; rx_packetread(apacket, pos, tlen, tix); /* get ticket */ /* * We allow the ticket to be optionally decoded by an alternate * ticket decoder, if the function variable * rxkad_AlternateTicketDecoder is set. That function should * return a code of -1 if it wants the ticket to be decoded by * the standard decoder. */ if (rxkad_AlternateTicketDecoder) { code = rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name, client.instance, client.cell, &sessionkey, &host, &start, &end); if (code && code != -1) { return code; } } else { code = -1; /* No alternate ticket decoder present */ } /* * If the alternate decoder is not present, or returns -1, then * assume the ticket is of the default style. */ if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5) || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) { code = tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock, kvno, client.name, client.instance, client.cell, &sessionkey, &host, &start, &end, tsp->flags & RXS_CONFIG_FLAGS_DISABLE_DOTCHECK); if (code) return code; } /* * If the alternate decoder/kerberos 5 decoder is not present, or * returns -1, then assume the ticket is of the default style. */ if (code == -1) { /* get ticket's key */ code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey); if (code) return RXKADUNKNOWNKEY; /* invalid kvno */ code = tkt_DecodeTicket(tix, tlen, &serverKey, client.name, client.instance, client.cell, &sessionkey, &host, &start, &end); if (code) return code; } code = tkt_CheckTimes(start, end, time(0)); if (code == 0) return RXKADNOAUTH; else if (code == -1) return RXKADEXPIRED; else if (code < -1) return RXKADBADTICKET; code = fc_keysched(&sessionkey, sconn->keysched); if (code) return RXKADBADKEY; memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec)); if (sconn->cksumSeen) { /* using v2 response */ afs_uint32 cksum; /* observed cksum */ struct rxkad_endpoint endpoint; /* connections endpoint */ int i; afs_uint32 xor[2]; memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32)); fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted), sconn->keysched, xor, DECRYPT); cksum = rxkad_CksumChallengeResponse(&v2r); if (cksum != v2r.encrypted.endpoint.cksum) return RXKADSEALEDINCON; (void)rxkad_SetupEndpoint(aconn, &endpoint); v2r.encrypted.endpoint.cksum = 0; if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0) return RXKADSEALEDINCON; for (i = 0; i < RX_MAXCALLS; i++) { v2r.encrypted.callNumbers[i] = ntohl(v2r.encrypted.callNumbers[i]); if (v2r.encrypted.callNumbers[i] < 0) return RXKADSEALEDINCON; } (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers); incChallengeID = ntohl(v2r.encrypted.incChallengeID); level = ntohl(v2r.encrypted.level); } else { /* expect old format response */ fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched, DECRYPT); incChallengeID = ntohl(oldr.encrypted.incChallengeID); level = ntohl(oldr.encrypted.level); } if (incChallengeID != sconn->challengeID + 1) return RXKADOUTOFSEQUENCE; /* replay attempt */ if ((level < sconn->level) || (level > rxkad_crypt)) return RXKADLEVELFAIL; sconn->level = level; rxkad_SetLevel(aconn, sconn->level); INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]); /* now compute endpoint-specific info used for computing 16 bit checksum */ rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq); /* otherwise things are ok */ sconn->expirationTime = end; sconn->authenticated = 1; if (tsp->user_ok) { code = tsp->user_ok(client.name, client.instance, client.cell, kvno); if (code) return RXKADNOAUTH; } else { /* save the info for later retreival */ int size = sizeof(struct rxkad_serverinfo); rock = (struct rxkad_serverinfo *)osi_Alloc(size); memset(rock, 0, size); rock->kvno = kvno; memcpy(&rock->client, &client, sizeof(rock->client)); sconn->rock = rock; } return 0; }
int rxkad_GetResponse(struct rx_securityClass *aobj, struct rx_connection *aconn, struct rx_packet *apacket) { struct rxkad_cprivate *tcp; char *tp; int v2; /* whether server is old style or v2 */ afs_int32 challengeID; rxkad_level level; char *response; int responseSize, missing; struct rxkad_v2ChallengeResponse r_v2; struct rxkad_oldChallengeResponse r_old; tcp = (struct rxkad_cprivate *)aobj->privateData; if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY; v2 = (rx_Contiguous(apacket) > sizeof(struct rxkad_oldChallenge)); tp = rx_DataOf(apacket); if (v2) { /* v2 challenge */ struct rxkad_v2Challenge *c_v2; if (rx_GetDataSize(apacket) < sizeof(struct rxkad_v2Challenge)) return RXKADPACKETSHORT; c_v2 = (struct rxkad_v2Challenge *)tp; challengeID = ntohl(c_v2->challengeID); level = ntohl(c_v2->level); } else { /* old format challenge */ struct rxkad_oldChallenge *c_old; if (rx_GetDataSize(apacket) < sizeof(struct rxkad_oldChallenge)) return RXKADPACKETSHORT; c_old = (struct rxkad_oldChallenge *)tp; challengeID = ntohl(c_old->challengeID); level = ntohl(c_old->level); } if (level > tcp->level) return RXKADLEVELFAIL; INC_RXKAD_STATS(challenges[rxkad_LevelIndex(tcp->level)]); if (v2) { int i; afs_uint32 xor[2]; memset((void *)&r_v2, 0, sizeof(r_v2)); r_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION); r_v2.spare = 0; (void)rxkad_SetupEndpoint(aconn, &r_v2.encrypted.endpoint); (void)rxi_GetCallNumberVector(aconn, r_v2.encrypted.callNumbers); for (i = 0; i < RX_MAXCALLS; i++) { if (r_v2.encrypted.callNumbers[i] < 0) return RXKADINCONSISTENCY; r_v2.encrypted.callNumbers[i] = htonl(r_v2.encrypted.callNumbers[i]); } r_v2.encrypted.incChallengeID = htonl(challengeID + 1); r_v2.encrypted.level = htonl((afs_int32) tcp->level); r_v2.kvno = htonl(tcp->kvno); r_v2.ticketLen = htonl(tcp->ticketLen); r_v2.encrypted.endpoint.cksum = rxkad_CksumChallengeResponse(&r_v2); memcpy((void *)xor, (void *)tcp->ivec, 2 * sizeof(afs_int32)); fc_cbc_encrypt(&r_v2.encrypted, &r_v2.encrypted, sizeof(r_v2.encrypted), tcp->keysched, xor, ENCRYPT); response = (char *)&r_v2; responseSize = sizeof(r_v2); } else { memset((void *)&r_old, 0, sizeof(r_old)); r_old.encrypted.incChallengeID = htonl(challengeID + 1); r_old.encrypted.level = htonl((afs_int32) tcp->level); r_old.kvno = htonl(tcp->kvno); r_old.ticketLen = htonl(tcp->ticketLen); fc_ecb_encrypt(&r_old.encrypted, &r_old.encrypted, tcp->keysched, ENCRYPT); response = (char *)&r_old; responseSize = sizeof(r_old); } if (RX_MAX_PACKET_DATA_SIZE < responseSize + tcp->ticketLen) return RXKADPACKETSHORT; /* not enough space */ rx_computelen(apacket, missing); missing = responseSize + tcp->ticketLen - missing; if (missing > 0) if (rxi_AllocDataBuf(apacket, missing, RX_PACKET_CLASS_SEND) > 0) return RXKADPACKETSHORT; /* not enough space */ /* copy response and ticket into packet */ rx_packetwrite(apacket, 0, responseSize, response); rx_packetwrite(apacket, responseSize, tcp->ticketLen, tcp->ticket); rx_SetDataSize(apacket, responseSize + tcp->ticketLen); return 0; }