mirror of
https://git.openafs.org/openafs.git
synced 2025-01-22 17:00:15 +00:00
ee56107d4d
In extractPioctlToken(), if we fail to allocate space for token_opaque_val, don't immediately pass the null pointer to osi_Free on the error exit path. Change-Id: Ic95e178cfbc1b1bbcb18701c0bbd5221426342ee Found-by: clang static analyzer with help from AFS_NONNULL Reviewed-on: http://gerrit.openafs.org/5262 Tested-by: BuildBot <buildbot@rampaginggeek.com> Reviewed-by: Derrick Brashear <shadow@dementix.org>
496 lines
13 KiB
C
496 lines
13 KiB
C
/*
|
|
* 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 <afsconfig.h>
|
|
#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));
|
|
osi_Assert(newToken != NULL);
|
|
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);
|
|
osi_Assert(rxkad->ticket != NULL);
|
|
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) {
|
|
if (opaque->token_opaque_val != NULL)
|
|
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;
|
|
}
|