/* 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; }