/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program 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 General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * provider-main.c * * Abstract: * * Likewise Security and Authentication Subsystem (LSASS) * * Active Directory Authentication Provider * * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com) * Sriram Nambakam (snambakam@likewisesoftware.com) * Wei Fu (wfu@likewisesoftware.com) * Brian Dunstan (bdunstan@likewisesoftware.com) * Kyle Stemen (kstemen@likewisesoftware.com) */ #include "adprovider.h" #include "provider-main.h" #ifdef HAVE_STRUCT_PSINFO // To determine whether the user is truly gone we have // to determine whether there are any active processes // owned by the user. // // Solaris and AIX have a /proc pseudo filesystem with // /proc/pid/psinfo files. DWORD LsaUmpIsUserActive( IN uid_t uUid, OUT BOOLEAN * pbUserIsActive ) { DWORD dwError = 0; BOOLEAN bFoundUserProcess = FALSE; DIR * dir = NULL; struct dirent * dirEntry = NULL; PSTR filePath = NULL; struct psinfo infoStruct; FILE * infoFile = NULL; BOOLEAN bFileExists; if ((dir = opendir("/proc")) == NULL) { LSA_LOG_DEBUG("LSA User Manager - opendir reported %u", errno); LSA_LOG_ERROR("LSA User Manager - unable to determine whether users have logged off."); dwError = LW_ERROR_CANNOT_DETECT_USER_PROCESSES; } BAIL_ON_LSA_ERROR(dwError); while (1) { errno = 0; dirEntry = readdir(dir); if (dirEntry == NULL) break; if ( dirEntry->d_name[0] == '.' ) continue; if(!isdigit((int)dirEntry->d_name[0])) continue; LW_SAFE_FREE_STRING(filePath); dwError = LwAllocateStringPrintf( &filePath, "/proc/%s/psinfo", dirEntry->d_name); BAIL_ON_LSA_ERROR(dwError); dwError = LsaCheckFileExists(filePath, &bFileExists); BAIL_ON_LSA_ERROR(dwError); if ( !bFileExists ) { // On AIX 6.1, a defunct process can lack a psinfo file. continue; } if ( infoFile ) { fclose(infoFile); infoFile = NULL; } if ( (infoFile = fopen(filePath, "r")) == NULL ) { continue; } if ( fread(&infoStruct, sizeof(infoStruct), 1, infoFile) == 1 ) { if ( uUid == infoStruct.pr_uid ) { bFoundUserProcess = TRUE; break; } } fclose(infoFile); infoFile = NULL; } *pbUserIsActive = bFoundUserProcess; cleanup: if ( infoFile ) fclose(infoFile); LW_SAFE_FREE_STRING(filePath); if ( dir ) closedir(dir); return dwError; error: *pbUserIsActive = TRUE; goto cleanup; } #elif HAVE_DECL_PSTAT_GETPROC // HPUX has no /proc pseudo filesystem but does // have pstat_getproc(). DWORD LsaUmpIsUserActive( IN uid_t uUid, OUT BOOLEAN * pbUserIsActive ) { DWORD dwError = 0; BOOLEAN bFoundUserProcess = FALSE; struct pst_status status[10]; int inBuffer; int i; // If status is too large pstat_getproc will // fail with EFAULT. inBuffer = pstat_getproc(status, sizeof(status[0]), sizeof(status)/sizeof(status[0]), 0); if ( inBuffer < 0 ) { LSA_LOG_DEBUG("LSA User Manager - pstat_getproc reported %u", errno); LSA_LOG_ERROR("LSA User Manager - unable to determine whether users have logged off."); dwError = LW_ERROR_CANNOT_DETECT_USER_PROCESSES; } BAIL_ON_LSA_ERROR(dwError); while ( inBuffer > 0 ) { for ( i = 0 ; i < inBuffer ; i++ ) { if ( uUid == status[i].pst_uid ) { bFoundUserProcess = TRUE; break; } } if ( bFoundUserProcess ) break; inBuffer = pstat_getproc(status, sizeof(status[0]), sizeof(status)/sizeof(status[0]), status[inBuffer - 1].pst_idx + 1); } *pbUserIsActive = bFoundUserProcess; cleanup: return dwError; error: *pbUserIsActive = TRUE; goto cleanup; } #elif defined(__LWI_DARWIN__) // Like FreeBSD, Apple has kvm_getprocs but it does // not work and sysctl must be used instead. DWORD LsaUmpIsUserActive( IN uid_t uUid, OUT BOOLEAN * pbUserIsActive ) { DWORD dwError = 0; BOOLEAN bFoundUserProcess = FALSE; int mib[4]; size_t len = 0; struct kinfo_proc *procs = NULL; // There are two calls to sysctl. The first is to obtain // the size of the required buffer. Because the data changes // frequently, the result is an estimate of what will be // needed and it rounds upwards. It's not until the second // call where the data is retrieved that an accurate size is // obtained. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_RUID; mib[3] = uUid; if ( sysctl(mib, 4, NULL, &len, NULL, 0) < 0 ) { LSA_LOG_DEBUG("LSA User Manager - sysctl reported %u", errno); LSA_LOG_ERROR("LSA User Manager - unable to determine whether users have logged off"); dwError = LW_ERROR_CANNOT_DETECT_USER_PROCESSES; } BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateMemory( len, (PVOID*)&procs); BAIL_ON_LSA_ERROR(dwError); if ( sysctl(mib, 4, procs, &len, NULL, 0) < 0 ) { LSA_LOG_DEBUG("LSA User Manager - sysctl reported %u", errno); LSA_LOG_ERROR("LSA User Manager - unable to determine whether users have logged off"); dwError = LW_ERROR_CANNOT_DETECT_USER_PROCESSES; } BAIL_ON_LSA_ERROR(dwError); if ( len > 0 ) { bFoundUserProcess = TRUE; } *pbUserIsActive = bFoundUserProcess; cleanup: LW_SAFE_FREE_MEMORY(procs); return dwError; error: *pbUserIsActive = TRUE; goto cleanup; } #elif HAVE_DECL_KVM_GETPROCS // FreeBSD has no /proc pseudo filesystem but does // have pstat_getproc(). #ifdef __LWI_FREEBSD__ #define LSA_UM_PROCESS_PATH "/dev/null" #else #define LSA_UM_PROCESS_PATH NULL #endif #ifdef KERN_PROC_RUID #define LSA_UM_PROCESS_RUID KERN_PROC_RUID #elif defined(KINFO_PROC_RUID) #define LSA_UM_PROCESS_RUID KINFO_PROC_RUID #else #error "unsupported KVM platform" #endif DWORD LsaUmpIsUserActive( IN uid_t uUid, OUT BOOLEAN * pbUserIsActive ) { DWORD dwError = 0; BOOLEAN bFoundUserProcess = FALSE; int filteredCount = 0; kvm_t * kd = NULL; struct kinfo_proc *procs = NULL; // Use of /dev/null is specific to FreeBSD. kd = kvm_open(NULL, LSA_UM_PROCESS_PATH, NULL, O_RDONLY, NULL); if (kd == NULL) { LSA_LOG_DEBUG("LSA User Manager - kvm_open reported %u", errno); LSA_LOG_ERROR("LSA User Manager - unable to determine whether users have logged off"); dwError = LW_ERROR_CANNOT_DETECT_USER_PROCESSES; } BAIL_ON_LSA_ERROR(dwError); procs = kvm_getprocs( kd, LSA_UM_PROCESS_RUID, uUid, &filteredCount); if ( procs && filteredCount > 0 ) { bFoundUserProcess = TRUE; } *pbUserIsActive = bFoundUserProcess; cleanup: if ( kd ) kvm_close(kd); return dwError; error: *pbUserIsActive = TRUE; goto cleanup; } #elif defined(__LWI_LINUX__) // Linux has a /proc pseudo filesystem but not // /proc/pid/psinfo files. DWORD LsaUmpIsUserActive( IN uid_t uUid, OUT BOOLEAN * pbUserIsActive ) { DWORD dwError = 0; BOOLEAN bFoundUserProcess = FALSE; DIR * dir = NULL; struct dirent * dirEntry = NULL; PSTR filePath = NULL; struct stat compareStat; int result = 0; if ((dir = opendir("/proc")) == NULL) { LSA_LOG_DEBUG("LSA User Manager - opendir reported %u", errno); LSA_LOG_ERROR("LSA User Manager - unable to determine whether users have logged off."); dwError = LW_ERROR_CANNOT_DETECT_USER_PROCESSES; } BAIL_ON_LSA_ERROR(dwError); while (1) { errno = 0; dirEntry = readdir(dir); if (dirEntry == NULL) break; if ( dirEntry->d_name[0] == '.' ) continue; if(!isdigit((int)dirEntry->d_name[0])) continue; LW_SAFE_FREE_STRING(filePath); dwError = LwAllocateStringPrintf( &filePath, "/proc/%s", dirEntry->d_name); BAIL_ON_LSA_ERROR(dwError); while( (result = stat(filePath, &compareStat)) < 0 ) { if ( errno != EINTR ) { break; } } if ( result == 0 ) { if ( uUid == compareStat.st_uid ) { bFoundUserProcess = TRUE; break; } } } *pbUserIsActive = bFoundUserProcess; cleanup: LW_SAFE_FREE_STRING(filePath); if ( dir ) closedir(dir); return dwError; error: *pbUserIsActive = TRUE; goto cleanup; } #else #error "unsupported platform" #endif