/*********************************************************************** * * Copyright (C) 2006 Novell, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, Novell, Inc. * * To contact Novell about this file by physical or electronic mail, * you may find current contact information at www.novell.com. * * Author: Juan Carlos Luciani * ***********************************************************************/ //===[ Include files ]===================================================== #include "internal.h" //===[ Type definitions ]================================================== // // Normalized Host Name Cache Entry definition // typedef struct _NormalizedHostNameCacheEntry { LIST_ENTRY listEntry; char *pHostName; char *pNormalizedHostName; int buffLengthRequired; } NormalizedHostNameCacheEntry, *PNormalizedHostNameCacheEntry; //===[ Type definitions for Local_sem ]==================================== // // Notes: Most of the code for this definitions and the Local_sem_xxxx // functions was copied with minor modifications from W. Richard // Stevens book: UNIX Network Programming, Interprocess // Communications (Printed in 1999). // // You may ask, why not just use Posix Named Semaphores? The answer // is that I wish that I could but I can not tolerate that they are // not released when the process that holds them terminates abnormally. // union semun /* define union for semctl() */ { int val; struct semid_ds *buf; unsigned short *array; }; typedef struct { int sem_semid; /* the System V semaphore ID */ int sem_magic; /* magic number if open */ } Local_sem_t; #ifndef SEM_R #define SEM_R 0400 #endif #ifndef SEM_A #define SEM_A 0200 #endif #define SVSEM_MODE (SEM_R | SEM_A | SEM_R>>3 | SEM_R>>6) #define SEM_MAGIC 0x45678923 #define SEM_FAILED ((Local_sem_t *)(-1)) /* avoid compiler warnings */ #ifndef SEMVMX #define SEMVMX 32767 /* historical System V max value for sem */ #endif #define MAX_OPEN_SEM_TRIES 10 /* for waiting for initialization */ //===[ Function prototypes ]=============================================== //===[ Global variables ]================================================== // Normalized host name cache variables static LIST_ENTRY normalizedHostNameCacheListHead; static pthread_mutex_t g_hNormalizedHostNameCacheMutex = PTHREAD_MUTEX_INITIALIZER; // Client configuration file folder char clientConfigFolder[] = "/etc/CASA/authtoken/client"; // Authentication mechanism configuration file folder char mechConfigFolder[] = "/etc/CASA/authtoken/client/mechanisms"; // Module Synchronization mutex pthread_mutex_t g_hModuleMutex = PTHREAD_MUTEX_INITIALIZER; // Path separator char pathCharString[] = "/"; // Milliseconds per System Tick static long g_milliSecondsPerTicks = 0; // Named Semaphore for user variables static char g_userNamedSemName[256]; static Local_sem_t *g_userNamedSem = SEM_FAILED; static bool g_userNamedSemAcquired = false; //++======================================================================= Local_sem_t* Local_sem_open(const char *pathname, int oflag, ... ) // // Arguments: // // Returns: // // Abstract: // // Notes: Most of the code for this routine was copied with minor // modifications from W. Richard Stevens book: UNIX Network // Programming, Interprocess Communications (Printed in 1999). // // L2 //=======================================================================-- { int i, fd, semflag, semid, save_errno; key_t key; mode_t mode; va_list ap; Local_sem_t *sem; union semun arg; unsigned int value; struct semid_ds seminfo; struct sembuf initop; /* 4no mode for sem_open() w/out O_CREAT; guess */ semflag = SVSEM_MODE; semid = -1; if (oflag & O_CREAT) { va_start(ap, oflag); /* init ap to final named argument */ mode = va_arg(ap, mode_t); value = va_arg(ap, unsigned int); va_end(ap); /* 4convert to key that will identify System V semaphore */ if ( (fd = open(pathname, oflag, mode)) == -1) return(SEM_FAILED); close(fd); if ( (key = ftok(pathname, 0)) == (key_t) -1) return(SEM_FAILED); semflag = IPC_CREAT | (mode & 0777); if (oflag & O_EXCL) semflag |= IPC_EXCL; /* 4create the System V semaphore with IPC_EXCL */ if ( (semid = semget(key, 1, semflag | IPC_EXCL)) >= 0) { /* 4success, we're the first so initialize to 0 */ arg.val = 0; if (semctl(semid, 0, SETVAL, arg) == -1) goto err; /* 4then increment by value to set sem_otime nonzero */ if (value > SEMVMX) { errno = EINVAL; goto err; } initop.sem_num = 0; initop.sem_op = value; initop.sem_flg = 0; if (semop(semid, &initop, 1) == -1) goto err; goto finish; } else if (errno != EEXIST || (semflag & IPC_EXCL) != 0) goto err; /* else fall through */ } /* * (O_CREAT not secified) or * (O_CREAT without O_EXCL and semaphore already exists). * Must open semaphore and make certain it has been initialized. */ if ( (key = ftok(pathname, 0)) == (key_t) -1) goto err; if ( (semid = semget(key, 0, semflag)) == -1) goto err; arg.buf = &seminfo; for (i = 0; i < MAX_OPEN_SEM_TRIES; i++) { if (semctl(semid, 0, IPC_STAT, arg) == -1) goto err; if (arg.buf->sem_otime != 0) goto finish; sleep(1); } errno = ETIMEDOUT; err: save_errno = errno; /* don't let semctl() change errno */ if (semid != -1) semctl(semid, 0, IPC_RMID); errno = save_errno; return(SEM_FAILED); finish: if ( (sem = malloc(sizeof(Local_sem_t))) == NULL) goto err; sem->sem_semid = semid; sem->sem_magic = SEM_MAGIC; return(sem); } //++======================================================================= int Local_sem_wait(Local_sem_t *sem) // // Arguments: // // Returns: // // Abstract: // // Notes: Most of the code for this routine was copied with minor // modifications from W. Richard Stevens book: UNIX Network // Programming, Interprocess Communications (Printed in 1999). // // L2 //=======================================================================-- { struct sembuf op; if (sem->sem_magic != SEM_MAGIC) { errno = EINVAL; return(-1); } op.sem_num = 0; op.sem_op = -1; //op.sem_flg = 0; op.sem_flg = SEM_UNDO; // Deviation from Richard's to allow cleanup in case of abnormal termination. if (semop(sem->sem_semid, &op, 1) < 0) return(-1); return(0); } //++======================================================================= int Local_sem_post(Local_sem_t *sem) // // Arguments: // // Returns: // // Abstract: // // Notes: Most of the code for this routine was copied with minor // modifications from W. Richard Stevens book: UNIX Network // Programming, Interprocess Communications (Printed in 1999). // // L2 //=======================================================================-- { struct sembuf op; if (sem->sem_magic != SEM_MAGIC) { errno = EINVAL; return(-1); } op.sem_num = 0; op.sem_op = 1; op.sem_flg = 0; if (semop(sem->sem_semid, &op, 1) < 0) return(-1); return(0); } //++======================================================================= int Local_sem_close(Local_sem_t *sem) // // Arguments: // // Returns: // // Abstract: // // Notes: Most of the code for this routine was copied with minor // modifications from W. Richard Stevens book: UNIX Network // Programming, Interprocess Communications (Printed in 1999). // // L2 //=======================================================================-- { if (sem->sem_magic != SEM_MAGIC) { errno = EINVAL; return(-1); } sem->sem_magic = 0; /* just in case */ free(sem); return(0); } //++======================================================================= DWORD GetTickCount(void) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { struct tms tm; DWORD tickCount; DbgTrace(2, "-GetTickCount- Start\n", 0); // Determine milliseconds per tick if we have not done already if (g_milliSecondsPerTicks == 0) { long ticksPerSecond; ticksPerSecond = sysconf(_SC_CLK_TCK); DbgTrace(3, "-GetTickCount- TicksPerSec = %0lX\n", ticksPerSecond); g_milliSecondsPerTicks = 1000 / ticksPerSecond; } // Determine the tickCount as milliseconds tickCount = g_milliSecondsPerTicks * times(&tm); DbgTrace(2, "-GetTickCount- End, retValue = %0lX\n", tickCount); return tickCount; } //++======================================================================= CasaStatus CreateUserMutex( HANDLE *phMutex ) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { CasaStatus retStatus = CasaStatusBuild(CASA_SEVERITY_ERROR, CASA_FACILITY_AUTHTOKEN, CASA_STATUS_UNSUCCESSFUL); DbgTrace(1, "-CreateUserMutex- Start\n", 0); // We use Named Semaphores to provide this functionality. The semaphore names are // linked to the user via its uid. if (sprintf(g_userNamedSemName, "/tmp/casa_auth_semuser_%d", geteuid()) != -1) { // Create or open semaphore to be only used by the effective user g_userNamedSem = Local_sem_open((const char*) g_userNamedSemName, O_RDWR | O_CREAT, 0600, 1); if (g_userNamedSem == SEM_FAILED) { DbgTrace(0, "-CreateUserMutex- Error opening named semaphore, errno = %d\n", errno); } else { // Success retStatus = CASA_STATUS_SUCCESS; } } else { DbgTrace(0, "-CreateUserMutex- sprintf failed, error = %d\n", errno); } DbgTrace(1, "-CreateUserMutex- End, retStatus\n", retStatus); return retStatus; } //++======================================================================= void AcquireUserMutex( HANDLE hMutex ) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(2, "-AcquireUserMutex- Start\n", 0); // Wait on the named semaphore if (Local_sem_wait(g_userNamedSem) != 0) { DbgTrace(0, "-AcquireUserMutex- Error returned by sem_wait(), errno = %d\n", errno); } // The user semaphore has been acquired g_userNamedSemAcquired = true; DbgTrace(2, "-AcquireUserMutex- End\n", 0); } //++======================================================================= void ReleaseUserMutex( HANDLE hMutex ) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(2, "-ReleaseUserMutex- Start\n", 0); // The user semaphore is no longer acquired g_userNamedSemAcquired = false; // Post on the named semaphore if (Local_sem_post(g_userNamedSem) != 0) { DbgTrace(0, "-ReleaseUserMutex- Error returned by sem_post(), errno = %d\n", errno); } DbgTrace(2, "-ReleaseUserMutex- End\n", 0); } //++======================================================================= void DestroyUserMutex( HANDLE hMutex ) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(2, "-DestroyUserMutex- Start\n", 0); // Do not do anything if the named semaphore is invalid if (g_userNamedSem != SEM_FAILED) { // Close the named semaphore. Note that we want user semaphores to // hang around, therefore we will not unlink them. This is per-design as // is not a resource leak. If someone has an issue with this, then it can // be solved by installing a cron job that cleans up the semaphores for // deleted users. if (Local_sem_close(g_userNamedSem) != 0) { DbgTrace(0, "-DestroyUserMutex- Error returned by sem_close(), errno = %d\n", errno); } // Forget about the semaphore g_userNamedSem = SEM_FAILED; } DbgTrace(2, "-DestroyUserMutex- End\n", 0); } //++======================================================================= LIB_HANDLE OpenLibrary( IN char *pFileName) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { LIB_HANDLE libHandle; DbgTrace(1, "-OpenLibrary- Start\n", 0); libHandle = dlopen(pFileName, RTLD_LAZY); if (libHandle == NULL) { DbgTrace(0, "-OpenLibrary- Not able to load library, error = %s\n", dlerror()); } DbgTrace(1, "-OpenLibrary- End, handle = %0lX\n", (long) libHandle); return libHandle; } //++======================================================================= void CloseLibrary( IN LIB_HANDLE libHandle) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(1, "-CloseLibrary- Start\n", 0); dlclose(libHandle); DbgTrace(1, "-CloseLibrary- End\n", 0); } //++======================================================================= void* GetFunctionPtr( IN LIB_HANDLE libHandle, IN char *pFunctionName) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { void *pFuncPtr; DbgTrace(1, "-GetFunctionPtr- Start\n", 0); pFuncPtr = dlsym(libHandle, pFunctionName); if (pFuncPtr == NULL) { DbgTrace(0, "-GetFunctionPtr- Not able to obtain func ptr, error = %s\n", dlerror()); } DbgTrace(1, "-GetFunctionPtr- End, pFuncPtr = %0lX\n", (long) pFuncPtr); return pFuncPtr; } //++======================================================================= char* NormalizeHostName( IN const char *pHostName) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { char *pNormalizedName = NULL; LIST_ENTRY *pListEntry; NormalizedHostNameCacheEntry *pEntry = NULL; DbgTrace(1, "-NormalizeHostName- Start\n", 0); // Obtain our synchronization mutex pthread_mutex_lock(&g_hNormalizedHostNameCacheMutex); // First try to find an entry in the normalized host name cache // for the host name provided. pListEntry = normalizedHostNameCacheListHead.Flink; while (pListEntry != &normalizedHostNameCacheListHead) { // Get pointer to the entry pEntry = CONTAINING_RECORD(pListEntry, NormalizedHostNameCacheEntry, listEntry); // Check if the entry is for the host name if (strcmp(pHostName, pEntry->pHostName) == 0) { // This entry corresponds to the given host name break; } else { // The entry does not correspond to the given host name pEntry = NULL; } // Advance to the next entry pListEntry = pListEntry->Flink; } // Check if we found an entry in our cache for the given host name if (pEntry) { // Entry found, obtain the normalized name from it. pNormalizedName = (char*) malloc(pEntry->buffLengthRequired); if (pNormalizedName) { // Copy the normalized name onto the allocated buffer strcpy(pNormalizedName, pEntry->pNormalizedHostName); } else { DbgTrace(0, "-NormalizeHostName- Buffer allocation error\n", 0); } } else { // An entry was not found in our cache, create one. pEntry = (NormalizedHostNameCacheEntry*) malloc(sizeof(NormalizedHostNameCacheEntry)); if (pEntry) { // Zero the entry memset(pEntry, 0, sizeof(*pEntry)); // Allocate a buffer to hold the host name in the entry pEntry->pHostName = (char*) malloc(strlen(pHostName) + 1); if (pEntry->pHostName) { struct hostent *pLookupResult; struct sockaddr_in sockAddr = {0}; // Copy the host name given into the allocated buffer strcpy(pEntry->pHostName, pHostName); // Now try to resolve the normalized name pLookupResult = gethostbyname(pHostName); if (pLookupResult && pLookupResult->h_addrtype == AF_INET && pLookupResult->h_length > 0 && pLookupResult->h_addr_list[0] != NULL) { char *pDnsHostName = (char*) malloc(NI_MAXHOST + 1); if (pDnsHostName) { // Set up a sockaddr structure sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = *((int*) pLookupResult->h_addr_list[0]); // Now try to resolve the name using DNS if (getnameinfo((const struct sockaddr*) &sockAddr, sizeof(sockAddr), pDnsHostName, NI_MAXHOST, NULL, 0, NI_NAMEREQD) == 0) { // We resolved the address to a DNS name, use it as the normalized name. pEntry->buffLengthRequired = (int) strlen(pDnsHostName) + 1; pEntry->pNormalizedHostName = (char*) malloc(pEntry->buffLengthRequired); if (pEntry->pNormalizedHostName) { // Copy the dns name strcpy(pEntry->pNormalizedHostName, pDnsHostName); } else { DbgTrace(0, "-NormalizeHostName- Buffer allocation error\n", 0); } } else { DbgTrace(0, "-NormalizeHostName- getnameInfo failed, error %d\n", errno); // Not able to resolve the name in DNS, just use the host name as // the normalized name. pEntry->buffLengthRequired = (int) strlen(pHostName) + 1; pEntry->pNormalizedHostName = (char*) malloc(pEntry->buffLengthRequired); if (pEntry->pNormalizedHostName) { // Copy the host name strcpy(pEntry->pNormalizedHostName, pHostName); } else { DbgTrace(0, "-NormalizeHostName- Buffer allocation error\n", 0); } } // Free the buffer allocated to hold the DNS name free(pDnsHostName); } else { DbgTrace(0, "-NormalizeHostName- Buffer allocation failure\n", 0); } } else { DbgTrace(0, "-NormalizeHostName- Name resolution failed, error = %d\n", errno); } } else { DbgTrace(0, "-NormalizeHostName- Buffer allocation error\n", 0); // Free the space allocated for the entry free(pEntry); } // Proceed based on whether or not we normalized the name if (pEntry->pNormalizedHostName) { // The name was normalized, save the entry in our cache. InsertHeadList(&normalizedHostNameCacheListHead, &pEntry->listEntry); // Return the normalized name present in the entry pNormalizedName = (char*) malloc(pEntry->buffLengthRequired); if (pNormalizedName) { // Copy the normalized name onto the allocated buffer strcpy(pNormalizedName, pEntry->pNormalizedHostName); } else { DbgTrace(0, "-NormalizeHostName- Buffer allocation error\n", 0); } } else { // The host name was not normalized, free allocated resources. if (pEntry->pHostName) free(pEntry->pHostName); free(pEntry); } } else { DbgTrace(0, "-NormalizeHostName- Buffer allocation error\n", 0); } } // Release our synchronization mutex pthread_mutex_unlock(&g_hNormalizedHostNameCacheMutex); DbgTrace(1, "-NormalizeHostName- End, pNormalizedName = %0lX\n", (long) pNormalizedName); return pNormalizedName; } //++======================================================================= CasaStatus InitializeHostNameNormalization(void) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { CasaStatus retStatus = CASA_STATUS_SUCCESS; DbgTrace(1, "-InitializeHostNameNormalization- Start\n", 0); // Initialize the cache list head InitializeListHead(&normalizedHostNameCacheListHead); DbgTrace(1, "-InitializeHostNameNormalization- End, retStatus = %08X\n", retStatus); return retStatus; } //++======================================================================= //++======================================================================= //++=======================================================================