/* * Copyright (c) 2010 Your Filesystem Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "afs/param.h" #include "afs/sysincludes.h" #include "afsincludes.h" #include "token.h" /* A jar for storing tokens in */ /*! * Return a token of the specified type from the selected tokenjar. * * @param[in] tokens * The tokenjar in which to search * @param[in] type * The type of token to return * * @return * A tokenUnion structure, from which the desired token can be * accessed using the appropriate element of the union. */ union tokenUnion * afs_FindToken(struct tokenJar *tokens, rx_securityIndex type) { while (tokens != NULL) { if (tokens->type == type) { return &tokens->content; } tokens = tokens->next; } return NULL; } /*! * Free a single token * * This will free the given token. No attempt is made to unlink * the token from its container, and it is an error to attempt to * free a token which is still linked. * * This performs a secure free, setting all token information to 0 * before returning allocated data blocks to the kernel. * * Intended primarily for internal use. * * @param[in] token * The token to free */ void afs_FreeOneToken(struct tokenJar *token) { if (token->next != NULL) osi_Panic("Freeing linked token"); switch (token->type) { case RX_SECIDX_KAD: if (token->content.rxkad.ticket != NULL) { memset(token->content.rxkad.ticket, 0, token->content.rxkad.ticketLen); afs_osi_Free(token->content.rxkad.ticket, token->content.rxkad.ticketLen); } break; default: break; } memset(token, 0, sizeof(struct tokenJar)); afs_osi_Free(token, sizeof(struct tokenJar)); } /*! * Free a token jar * * Free all of the tokens in a given token jar. This will also set the * pointer to the jar to NULL, to indicate that it has been freed. * * @param[in] tokenPtr * A pointer to the address of the tokenjar to free. */ void afs_FreeTokens(struct tokenJar **tokenPtr) { struct tokenJar *next, *tokens; tokens = *tokenPtr; *tokenPtr = NULL; while(tokens != NULL) { next = tokens->next; tokens->next = NULL; /* Unlink from chain */ afs_FreeOneToken(tokens); tokens = next; } } /*! * Add a token to a token jar * * Add a new token to a token jar. If the jar already exists, * then this token becomes the first in the jar. If it doesn't * exist, then a new jar is created. The contents of the new * token are initialised to 0 upon creation. * * @param[in] tokens * A pointer to the address of the token jar to populate * @param[in] type * The type of token to create * * @return * A pointer to the tokenUnion of the newly created token, * which may then be used to populate the token. */ union tokenUnion * afs_AddToken(struct tokenJar **tokens, rx_securityIndex type) { struct tokenJar *newToken; newToken = afs_osi_Alloc(sizeof(struct tokenJar)); memset(newToken, 0, sizeof(*newToken)); newToken->type = type; newToken->next = *tokens; *tokens = newToken; return &newToken->content; } /*! * Indicate if a single token is expired * * @param[in] token * The token to check * @param[in] now * The time to check against for expiry (typically the results of * calling osi_Time()) * * @returns * True if the token has expired, false otherwise */ int afs_IsTokenExpired(struct tokenJar *token, afs_int32 now) { switch (token->type) { case RX_SECIDX_KAD: if (token->content.rxkad.clearToken.EndTimestamp < now - NOTOKTIMEOUT) return 1; break; default: return 0; } return 0; /* not reached, but keep gcc happy */ } /*! * Indicate if a token is usable by the kernel module * * This determines whether a token is usable. A usable token is one that * has not expired, and which is otherwise suitable for use. * * @param[in] token * The token to check * @param[in] now * The time to use for the expiry check * * @returns * True if the token is usable, false otherwise */ int afs_IsTokenUsable(struct tokenJar *token, afs_int32 now) { if (afs_IsTokenExpired(token, now)) return 0; switch (token->type) { case RX_SECIDX_KAD: /* We assume that all non-expired rxkad tokens are usable by us */ return 1; default : return 0; } } /*! * Discard all expired tokens from a token jar * * This permanently removes all tokens which have expired from the token * jar. Note that tokens which are not usable, but which have not expired, * will not be deleted. * * @param[in] tokenPtr * A pointer to the address of the token jar to check * @param[in] now * The time to use for the expiry check */ void afs_DiscardExpiredTokens(struct tokenJar **tokenPtr, afs_int32 now) { struct tokenJar *next; while (*tokenPtr != NULL) { if (afs_IsTokenExpired(*tokenPtr, now)) { next = (*tokenPtr)->next; (*tokenPtr)->next = NULL; afs_FreeOneToken(*tokenPtr); *tokenPtr = next; } else { tokenPtr = &(*tokenPtr)->next; } } } /*! * Indicate whether a token jar contains one, or more usable tokens * * @param[in] token * The token jar to check * @param[in] now * The cime to use for the expiry check * * @returns * True if the jar contains usable tokens, otherwise false */ int afs_HasUsableTokens(struct tokenJar *token, afs_int32 now) { while (token != NULL) { if (afs_IsTokenUsable(token, now)) return 1; token = token->next; } return 0; } /*! * Indicate whether a token jar contains a valid (non-expired) token * * @param[in] token * The token jar to check * @param[in] now * The time to use for the expiry check * * @returns * True if the jar contains valid tokens, otherwise false * */ int afs_HasValidTokens(struct tokenJar *token, afs_int32 now) { while (token != NULL) { if (!afs_IsTokenExpired(token, now)) return 1; token = token->next; } return 0; } /*! * Count the number of valid tokens in a jar. A valid token is * one which is not expired - note that valid tokens may not be * usable by the kernel. * * @param[in] token * The token jar to check * @param[in] now * The time to use for the expiry check * * @returns * The number of valid tokens in the jar */ static int countValidTokens(struct tokenJar *token, time_t now) { int count = 0; while (token != NULL) { if (!afs_IsTokenExpired(token, now)) count ++; token = token->next; } return count; } /*! * Add an rxkad token to the token jar * * @param[in] tokens * A pointer to the address of the jar to add the token to * @param[in] ticket * A data block containing the token's opaque ticket * @param[in] ticketLen * The length of the ticket data block * @param[in] clearToken * The cleartext token information */ void afs_AddRxkadToken(struct tokenJar **tokens, char *ticket, int ticketLen, struct ClearToken *clearToken) { union tokenUnion *tokenU; struct rxkadToken *rxkad; tokenU = afs_AddToken(tokens, RX_SECIDX_KAD); rxkad = &tokenU->rxkad; rxkad->ticket = afs_osi_Alloc(ticketLen); rxkad->ticketLen = ticketLen; memcpy(rxkad->ticket, ticket, ticketLen); rxkad->clearToken = *clearToken; } static int afs_AddRxkadTokenFromPioctl(struct tokenJar **tokens, struct ktc_tokenUnion *pioctlToken) { struct ClearToken clear; clear.AuthHandle = pioctlToken->ktc_tokenUnion_u.at_kad.rk_kvno; clear.ViceId = pioctlToken->ktc_tokenUnion_u.at_kad.rk_viceid; clear.BeginTimestamp = pioctlToken->ktc_tokenUnion_u.at_kad.rk_begintime; clear.EndTimestamp = pioctlToken->ktc_tokenUnion_u.at_kad.rk_endtime; memcpy(clear.HandShakeKey, pioctlToken->ktc_tokenUnion_u.at_kad.rk_key, 8); afs_AddRxkadToken(tokens, pioctlToken->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_val, pioctlToken->ktc_tokenUnion_u.at_kad.rk_ticket.rk_ticket_len, &clear); /* Security means never having to say you're sorry */ memset(clear.HandShakeKey, 0, 8); return 0; } static int rxkad_extractTokenForPioctl(struct tokenJar *token, struct ktc_tokenUnion *pioctlToken) { struct token_rxkad *rxkadPioctl; struct rxkadToken *rxkadInternal; rxkadPioctl = &pioctlToken->ktc_tokenUnion_u.at_kad; rxkadInternal = &token->content.rxkad; rxkadPioctl->rk_kvno = rxkadInternal->clearToken.AuthHandle; rxkadPioctl->rk_viceid = rxkadInternal->clearToken.ViceId; rxkadPioctl->rk_begintime = rxkadInternal->clearToken.BeginTimestamp; rxkadPioctl->rk_endtime = rxkadInternal->clearToken.EndTimestamp; memcpy(rxkadPioctl->rk_key, rxkadInternal->clearToken.HandShakeKey, 8); rxkadPioctl->rk_ticket.rk_ticket_val = xdr_alloc(rxkadInternal->ticketLen); if (rxkadPioctl->rk_ticket.rk_ticket_val == NULL) return ENOMEM; rxkadPioctl->rk_ticket.rk_ticket_len = rxkadInternal->ticketLen; memcpy(rxkadPioctl->rk_ticket.rk_ticket_val, rxkadInternal->ticket, rxkadInternal->ticketLen); return 0; } /*! * Add a token to a token jar based on the input from a new-style * SetToken pioctl * * @param[in] tokens * Pointer to the address of a token jar * @param[in] pioctlToken * The token structure obtained through the pioctl (note this * is a single, XDR decoded, token) * * @returns * 0 on success, an error code on failure */ int afs_AddTokenFromPioctl(struct tokenJar **tokens, struct ktc_tokenUnion *pioctlToken) { switch (pioctlToken->at_type) { case RX_SECIDX_KAD: return afs_AddRxkadTokenFromPioctl(tokens, pioctlToken); } return EINVAL; } static int extractPioctlToken(struct tokenJar *token, struct token_opaque *opaque) { XDR xdrs; struct ktc_tokenUnion *pioctlToken; int code; memset(opaque, 0, sizeof(token_opaque)); pioctlToken = osi_Alloc(sizeof(struct ktc_tokenUnion)); if (pioctlToken == NULL) return ENOMEM; pioctlToken->at_type = token->type; switch (token->type) { case RX_SECIDX_KAD: code = rxkad_extractTokenForPioctl(token, pioctlToken); break; default: code = EINVAL;; } if (code) goto out; xdrlen_create(&xdrs); if (!xdr_ktc_tokenUnion(&xdrs, pioctlToken)) { code = EINVAL; xdr_destroy(&xdrs); goto out; } opaque->token_opaque_len = xdr_getpos(&xdrs); xdr_destroy(&xdrs); opaque->token_opaque_val = osi_Alloc(opaque->token_opaque_len); if (opaque->token_opaque_val == NULL) { code = ENOMEM; goto out; } xdrmem_create(&xdrs, opaque->token_opaque_val, opaque->token_opaque_len, XDR_ENCODE); if (!xdr_ktc_tokenUnion(&xdrs, pioctlToken)) { code = EINVAL; xdr_destroy(&xdrs); goto out; } xdr_destroy(&xdrs); out: xdr_free((xdrproc_t) xdr_ktc_tokenUnion, &pioctlToken); osi_Free(pioctlToken, sizeof(struct ktc_tokenUnion)); if (code != 0) { osi_Free(opaque->token_opaque_val, opaque->token_opaque_len); opaque->token_opaque_val = NULL; opaque->token_opaque_len = 0; } return code; } int afs_ExtractTokensForPioctl(struct tokenJar *token, time_t now, struct ktc_setTokenData *tokenSet) { int numTokens, pos; int code = 0; numTokens = countValidTokens(token, now); tokenSet->tokens.tokens_len = numTokens; tokenSet->tokens.tokens_val = xdr_alloc(sizeof(struct token_opaque) * numTokens); if (tokenSet->tokens.tokens_val == NULL) return ENOMEM; pos = 0; while (token != NULL && pos < numTokens) { code = extractPioctlToken(token, &tokenSet->tokens.tokens_val[pos]); if (code) goto out; token = token->next; pos++; } out: if (code) xdr_free((xdrproc_t) xdr_ktc_setTokenData, tokenSet); return code; }