Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

874 lines
19 KiB
C

static char sccsid[] = "@(#)91 1.6 src/bos/usr/ccs/lib/libs/userlicense.c, libs, bos41B, 412_41B_sync 1/4/95 14:46:10";
/*
* COMPONENT_NAME: LIBS
*
* FUNCTIONS: _FixedGetAllLicenses
* _FixedLicenseCount
* _FixedReleaseLicense
* _FixedRequestLicense
* _FloatingLogTimeout
* _FloatingRequestLicense
* _GetLicense
* _LLAddData
* _LLDestroy
* _LLInitialize
* _LoggedOn
* _ReleaseLicense
* _failure_handler
* _success_handler
* _timeout_handler
*
* ORIGINS: 27
*
* IBM CONFIDENTIAL -- (IBM Confidential Restricted when
* combined with the aggregated modules for this product)
* SOURCE MATERIALS
*
* (C) COPYRIGHT International Business Machines Corp. 1993,1994
* All Rights Reserved
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <sys/sem.h>
#include <usersec.h> /* For SEC_INT */
#include <userconf.h> /* For SC_MAXLOGINS */
#include <utmp.h>
#include <sys/file.h>
#include <sys/lockf.h>
#include <sys/mode.h>
#include <sys/ipc.h>
#include <sys/license.h>
#include <uuid.h>
#include <libnetls.h>
enum { NoLicense, FixedLicense, FloatingLicense };
/*
* LicenseType is set to one of the 3 enum values above. It is
* then used to determine what license type to free in _ReleaseLicense().
*/
static int LicenseType = NoLicense;
/***************************************************************************/
/***************************** Fixed License *******************************/
/***************************************************************************/
/*
* LICENSE_MAXIMUM should always be the largest number that a semaphore
* can handle.
*/
#define LICENSE_MINIMUM 2
#define LICENSE_TIERTOP 128
#define LICENSE_MAXIMUM SHRT_MAX
#define INIT_PROGRAM "/usr/sbin/init"
/*
* Defect 168205: The CHLICENSE_PROGRAM is used to path
* the chlicense command. If chlicense exists, then we
* assume this is a server machine and everything is normal.
* Else, if chlicense doesn't exist (stat() fails) we
* assume that this is a client machine and hardcode the
* license count to 2.
*/
#define CHLICENSE_PROGRAM "/usr/bin/chlicense"
#undef NODELOCK_LIC
#ifdef NODELOCK_LIC
/*
* The following code supports nodelock licenses.
*/
/*
* INFO_SZ defines the size of the iFOR/LS fetch array.
*/
#define INFO_SIZE 50
typedef struct nodelock_info
{
nls_lic_annot_t lic_annot [INFO_SIZE];
nls_time_t start_date[INFO_SIZE];
nls_time_t exp_date [INFO_SIZE];
nls_time_t ts [INFO_SIZE];
long prodid [INFO_SIZE];
} NLInfo;
/*
* Structures used to store non-dup license information.
*/
typedef struct per_license_data
{
nls_time_t start;
nls_time_t expire;
nls_time_t ts;
int annot;
} PLData;
typedef struct license_list
{
int Allocated; /* Total entries allocated for PLD. */
int Used; /* Total entries used in PLD. */
PLData *PLD;
} LicenseList;
/*
* NAME: _LLInitialize
*
* FUNCTION: Initializes a LicenseList structure.
*
* RETURNS: Nothing.
*/
static void
_LLInitialize(LicenseList *ll)
{
ll->PLD = (PLData *) 0;
ll->Allocated = ll->Used = 0;
}
/*
* NAME: _LLDestroy
*
* FUNCTION: Resets the values of a LicenseList structure.
*
* RETURNS: Nothing.
*/
static void
_LLDestroy(LicenseList *ll)
{
if (ll->PLD) free(ll->PLD);
_LLInitialize(ll);
}
/*
* NAME: _LLAddData
*
* FUNCTION: Adds a PLData entry to a LicenseList. It extends the list
* if the list is full.
*
* RETURNS: 0 success
* -1 failure
*/
static int
_LLAddData(LicenseList *ll, PLData *pld)
{
PLData *tmp;
int rc = 0;
/*
* Increase the size of ll->PLD if the new pld won't fit.
*/
if (ll->Used >= ll->Allocated)
{
if (!ll->Allocated)
{
if (!(ll->PLD = (PLData *) malloc(
INFO_SIZE * sizeof(PLData))))
{
rc = -1;
goto fast_exit;
}
}
else
{
if (!(tmp = (PLData *) realloc(ll->PLD,
(ll->Allocated + INFO_SIZE) * sizeof(PLData))))
{
rc = -1;
goto fast_exit;
}
ll->PLD = tmp;
}
ll->Allocated += INFO_SIZE;
}
memcpy(&ll->PLD[ ll->Used++ ], pld, sizeof(PLData));
fast_exit:
return(rc);
}
/*
* NAME: _FixedGetAllLicenses
*
* FUNCTION: Get all fixed licenses. If MaxLicense is a postive value,
* stop once MaxLicense is reached.
*
* NOTES: Fetches information for the valid nodelocked licenses for this
* product into arrays in INFO_SIZE chunks removing duplicates as
* it goes.
*
* RETURNS: 0 or greater - The number of licenses.
* -1 - Failure.
*/
static int
_FixedGetAllLicenses(int MaxLicenses)
{
nls_vnd_id_t vid = VENDOR_ID;
nls_job_id_t job_id;
LicenseList ll;
PLData pld;
NLInfo *nl = (NLInfo *) 0;
struct stat sbuf;
long nl_recs; /* number of nodelocked files fetched */
long remaining;
long pntr = 0;
long status;
long i;
int j;
int Licenses = 0;
_LLInitialize(&ll);
/*
* If the monitord program does not exist, then ignore the
* nodelock file data.
* netls_init() will take 75 seconds to timeout if NODELOCK_FILE
* does not exist.
*/
if (stat(MONITORD_PROGRAM, &sbuf) || stat(NODELOCK_FILE, &sbuf))
goto fast_exit;
/*
* Initialize NetLS nodelock information.
*/
memset(&job_id, 0, sizeof(job_id));
netls_init(vid, (long) VENDOR_KEY, &job_id, &status);
if (status < 0L)
{
Licenses = -1;
goto fast_exit;
}
/*
* Get memory for the netls_get_nodelock_info() data.
*/
if (!(nl = (NLInfo *) malloc(sizeof(NLInfo))))
{
Licenses = -1;
goto fast_exit;
}
do
{
nl_recs = netls_get_nodelock_info(&job_id, (long) INFO_SIZE,
&pntr, nl->prodid, nl->start_date,
nl->exp_date, nl->ts, nl->lic_annot,
&remaining, &status);
if (status < 0L)
{
Licenses = -1;
goto fast_exit;
}
/*
* For each entry in nl, if it is not already in ll->PLD,
* then add it to ll->PLD.
*/
for (i = 0L; i < nl_recs; i++)
{
/* Only AIX nodelock product IDs are to be counted */
if (nl->prodid[i] != NL_PRODUCT_ID)
continue;
/* Convert to a pld */
pld.start = nl->start_date[i];
pld.expire = nl->exp_date[i];
pld.ts = nl->ts[i];
pld.annot = atoi(&nl->lic_annot[i][sizeof("AIX ")-1]);
/* Ignore negative license values. */
if (pld.annot < 0)
continue;
for (j = 0; j < ll.Used; j++)
{
if (!memcmp(&pld, &ll.PLD[j], sizeof(PLData)))
break;
}
if (j >= ll.Used)
{
if (_LLAddData(&ll, &pld))
{
Licenses = -1;
goto fast_exit;
}
Licenses += pld.annot;
/*
* If we don't need to know about any more
* licenses, then stop.
*/
if (Licenses >= MaxLicenses && MaxLicenses > 0)
goto fast_exit;
}
}
} while (pntr);
fast_exit:
if (nl) free(nl);
_LLDestroy(&ll);
return(Licenses);
}
/*
* NAME: _FixedLicenseCount
*
* FUNCTION: Returns the number of fixed licenses available.
*
* RETURNS: LICENSE_MINIMUM or greater (Number of licenses)
*/
int
_FixedLicenseCount(void)
{
register int rc;
register int Licenses = LICENSE_MINIMUM;
if ((rc=_FixedGetAllLicenses(LICENSE_TIERTOP - LICENSE_MINIMUM)) >= 0)
{
Licenses += rc;
if (Licenses >= LICENSE_TIERTOP)
Licenses = LICENSE_MAXIMUM;
}
return(Licenses);
}
#else /* !NODELOCK_LIC */
/*
* NAME: _FixedLicenseCount
*
* FUNCTION: Returns the number of fixed licenses available.
*
* RETURNS: maxlogins or greater (Number of licenses)
*/
int
_FixedLicenseCount(void)
{
int maxlogins;
int Licenses = LICENSE_MAXIMUM;
if (!getconfattr("usw", SC_MAXLOGINS, &maxlogins, SEC_INT) &&
(maxlogins > 0 && maxlogins <= LICENSE_MAXIMUM))
{
Licenses = maxlogins;
}
return(Licenses);
}
#endif /* NODELOCK_LIC */
/*
* NAME: _FixedReleaseLicense
*
* FUNCTION: Releases a fixed license.
*
* RETURNS: Nothing.
*/
static void
_FixedReleaseLicense(void)
{
key_t key;
struct sembuf sop = { 0, 1, (SEM_UNDO|IPC_NOWAIT) };
int semid;
/*
* Create the semaphore key from the init program.
*/
if ((key = ftok(INIT_PROGRAM, 1)) != (key_t) -1)
if ((semid = semget(key, 1, 0)) != -1)
semop(semid, &sop, 1); /* Increment the semaphore */
}
/*
* NAME: _FixedRequestLicense
*
* FUNCTION: Obtains a fixed user license, if one is available.
*
* RETURNS: 1 on success
* 0 on failure
*/
static int
_FixedRequestLicense(void)
{
key_t key;
struct sembuf sop = { 0, -1, (SEM_UNDO|IPC_NOWAIT) };
struct sembuf initsop = { 0, 0, IPC_NOWAIT };
struct stat sbuf;
int semid;
int rc = 1;
/*
* If we can't create a semaphore key from the init program,
* then allow the user access.
*/
if ((key = ftok(INIT_PROGRAM, 1)) == (key_t) -1)
goto fast_exit;
/*
* Create a semaphore ID. If none existed, then initialize the
* semaphore. If one existed, then call semget() again with no flags.
* This ordering is purposeful to prevent competing logins from
* conflicting. (If semget() came before semget(CREAT), competing
* logins may both fail the first semget() and only 1 login would
* succeed on the semget(CREAT). With the ordering below, they
* both should always succeed.)
*/
if ((semid = semget(key, 1, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR)) != -1)
{
#ifdef NODELOCK_LIC
#ifdef FUTURE
int maxlogins;
#endif /* FUTURE */
initsop.sem_op = _FixedLicenseCount();
#ifdef FUTURE
/*
* maxlogins is used to limit the number of fixed licenses
* the administrator wants to use on his/her system. The
* system may have 100 licenses, but the administrator
* may only want 50 logged in at any given time. This
* assumes floating licensing is disabled.
*/
if (!getconfattr("usw", SC_MAXLOGINS, &maxlogins, SEC_INT))
if (maxlogins >= 0 && maxlogins < initsop.sem_op)
initsop.sem_op = maxlogins;
#endif /* FUTURE */
semop(semid, &initsop, 1);
#else /* !NODELOCK_LIC */
int maxlogins;
/* Defect 168205: Check for existence of the
* chlicense command . If
* the stat() fails (chlicense doesn't exist)
* assume this is a client machine and set the
* license count to LICENSE_MINIMUM (2)
* (see defect).
*/
if (stat(CHLICENSE_PROGRAM, &sbuf))
{
initsop.sem_op = LICENSE_MINIMUM;
semop(semid, &initsop, 1);
}
else
{
if (!getconfattr("usw", SC_MAXLOGINS, &maxlogins, SEC_INT))
{
initsop.sem_op =
(maxlogins > 0 && maxlogins <= LICENSE_MAXIMUM)
? maxlogins : LICENSE_MAXIMUM;
semop(semid, &initsop, 1);
}
else
{
/*
* Couldn't get the maxlogins attribute, so
* remove the semaphore and perform no
* licensing checks.
*/
semctl(semid, 1, IPC_RMID, 0);
goto fast_exit;
}
}
#endif /* NODELOCK_LIC */
}
else
if ((semid = semget(key, 1, 0)) == -1)
{
/*
* If we can't access the semaphore, then allow the user access.
*/
goto fast_exit;
}
/*
* Decrement the semaphore counter and fail if it reaches zero.
*/
if (semop(semid, &sop, 1) != -1)
LicenseType = FixedLicense;
else
rc = 0; /* Fail */
fast_exit:
return(rc);
}
/***************************************************************************/
/**************************** Floating License *****************************/
/***************************************************************************/
#define NETLS_WAITING 2
#define NETLS_SUCCESS 1
#define NETLS_FAILURE 0
#define NETLS_TIMEOUT (-1)
#define REQUEST_TIMEOUT 60
/*
* This variable is volatile because the value will be changed by
* interrupts. Since the compiler may change the ordering of when
* this variable is set with respect to other lines of code and I
* don't want it to, I've declared it volatile to force the compiler
* to keep the ordering as I've specified.
*/
static volatile int lic_rc;
/*
* NAME: _success_handler
*
* FUNCTION: On SIGUSR1, sets lic_rc to NETLS_SUCCESS.
*
* RETURNS: Nothing.
*/
static void
_success_handler(void)
{
lic_rc = NETLS_SUCCESS;
}
/*
* NAME: _failure_handler
*
* FUNCTION: On SIGUSR2, sets lic_rc to NETLS_FAILURE.
*
* RETURNS: Nothing.
*/
static void
_failure_handler(void)
{
lic_rc = NETLS_FAILURE;
}
/*
* NAME: _timeout_handler
*
* FUNCTION: On SIGALRM, sets lic_rc to NETLS_TIMEOUT.
*
* RETURNS: Nothing.
*/
static void
_timeout_handler(void)
{
/* Don't wipe out a good answer if we received one. */
if (lic_rc == NETLS_WAITING)
lic_rc = NETLS_TIMEOUT;
}
/*
* NAME: _FloatingLogTimeout
*
* FUNCTION: Log timeouts in the netls log file.
*
* RETURNS: Nothing.
*/
static void
_FloatingLogTimeout(void)
{
char log_msg[250]; /* err msg and some other logging information */
int log_msgl; /* parameters for api calls */
int timeout_log;
int lockptr;
time_t now;
/*
* Add the current time.
*/
time(&now);
strcpy(log_msg, ctime(&now));
log_msg[strlen(log_msg) - 1] = '\0';
/*
* Add message.
*/
strcat(log_msg, " ");
strcat(log_msg, "RESPONSE FROM LICENSE SERVER TIMED OUT\n");
strcat(log_msg, " : hardstop policy. login denied\n");
log_msgl= strlen(log_msg);
/*
* If we can't open(), dup(), lseek(), lockf(), and/or write(),
* don't worry about it. It's not the end of the world!
*/
timeout_log = open(NETLS_ERROR_LOG, O_WRONLY|O_CREAT|O_APPEND,
S_IRUSR | S_IWUSR);
if (timeout_log >= 0)
{
if ((lockptr = dup(timeout_log)) >= 0)
{
lseek(lockptr, 0L, SEEK_SET);
lockf(lockptr, F_LOCK, 0L);
}
write(timeout_log, log_msg, log_msgl);
if (lockptr >= 0)
{
lseek(lockptr, 0L, SEEK_SET);
lockf(lockptr, F_ULOCK, 0L);
close(lockptr);
}
close(timeout_log);
}
}
/*
* NAME: _FloatingRequestLicense
*
* FUNCTION: Obtains a floating user license, if one is available.
*
* RETURNS: 1 on success (NETLS_SUCCESS).
* 0 on failure (NETLS_FAILURE).
*/
static int
_FloatingRequestLicense(void)
{
struct monitord_request client_request;
struct sigaction new_alarm, old_alarm;
struct sigaction new_user1, old_user1;
struct sigaction new_user2, old_user2;
int beforetime, aftertime;
int fd;
lic_rc = NETLS_FAILURE;
if ((fd = open(MONITORD_PIPE, O_RDWR|O_NONBLOCK, S_IRUSR|S_IWUSR)) >= 0)
{
/* Create the request data. */
client_request.login_pid = getpid();
client_request.request_type = REQUEST_LIC;
/*
* Setup signals SIGUSR1, SIGUSR2, and SIGALRM.
*/
new_user1.sa_handler = _success_handler;
new_user1.sa_flags = 0;
sigemptyset(&(new_user1.sa_mask));
sigaction(SIGUSR1, &new_user1, &old_user1);
new_user2.sa_handler = _failure_handler;
new_user2.sa_flags = 0;
sigemptyset(&(new_user2.sa_mask));
sigaction(SIGUSR2, &new_user2, &old_user2);
new_alarm.sa_handler = _timeout_handler;
new_alarm.sa_flags = 0;
sigemptyset(&(new_alarm.sa_mask));
sigaction(SIGALRM, &new_alarm, &old_alarm);
/*
* Set lic_rc after the signals have been initialized,
* just in case erroneous signals change the value.
*/
lic_rc = NETLS_WAITING;
/* Turn on the timer. */
beforetime = alarm(REQUEST_TIMEOUT);
/*
* Send the message to monitord and wait for response.
*/
write(fd, &client_request, sizeof(client_request));
while (lic_rc == NETLS_WAITING)
pause();
/* Turn off the alarm. */
aftertime = alarm(0);
/*
* Reset the signals.
*/
sigaction(SIGUSR1, &old_user1, (struct sigaction *) 0);
sigaction(SIGUSR2, &old_user2, (struct sigaction *) 0);
sigaction(SIGALRM, &old_alarm, (struct sigaction *) 0);
/*
* If there was a timer in progress before we set our timer,
* then restore the original timer and account for our usage.
*/
if (beforetime > 0)
{
aftertime = beforetime - (REQUEST_TIMEOUT - aftertime);
if (aftertime <= 0) aftertime = 1;
alarm(aftertime);
}
/*
* Handle timeouts.
*/
if (lic_rc == NETLS_TIMEOUT)
{
/* Send a release message. */
client_request.request_type = RELEASE_LIC;
write(fd, &client_request, sizeof(client_request));
_FloatingLogTimeout();
lic_rc = NETLS_FAILURE;
}
close(fd);
}
if (lic_rc == NETLS_SUCCESS)
LicenseType = FloatingLicense;
return(lic_rc);
}
/***************************************************************************/
/*************************** License Manager *******************************/
/***************************************************************************/
/*
* NAME: _LoggedOn
*
* FUNCTION: Determines if the user is already logged onto this device.
*
* RETURNS: 1 if user is logged on.
* 0 if user is not logged on.
*/
static int
_LoggedOn(char *tty)
{
struct utmp *ut;
int rc = 0;
/*
* If the first process associated with this device is a
* USER_PROCESS, then no license is required.
*/
if (!strncmp(tty, "/dev/", 5))
tty += 5;
setutent();
while (ut = getutent())
{
if (((ut->ut_type == LOGIN_PROCESS) ||
(ut->ut_type == USER_PROCESS) ||
(ut->ut_type == INIT_PROCESS)) &&
!strncmp(ut->ut_line, tty, sizeof(ut->ut_line)))
{
if (ut->ut_type == USER_PROCESS)
rc = 1;
break;
}
}
endutent();
return(rc);
}
/*
* NAME: _GetLicense
*
* FUNCTION: Try to obtain a fixed license. If that fails try to obtain
* a floating license. If both fail return failure; otherwise
* return success.
*
* RETURNS: 1 on success.
* 0 on failure.
*/
int
_GetLicense(char *tty, char *uname)
{
struct stat sbuf;
int rc = 1;
/*
* If we already have a license for this process, we don't need
* a new one. This check prevents the same process from obtaining
* multiple licenses.
*/
if (LicenseType != NoLicense)
goto fast_exit;
#ifdef MUTUALLY_EXCLUSIVE
#error Wasn't expecting MUTUALLY_EXCLUSIVE to be defined.
/*
* Either FIXED or FLOAT, but NOT BOTH.
* o) The "root" user name does not require a license.
* o) If the user is already logged onto this device,
* no license is needed.
* o) For non-root users, if the FLOATING_ON file does not exist,
* then obtain a fixed license.
* o) Else obtain a floating license.
*/
if (strcmp(uname, "root") && !_LoggedOn(tty))
rc = (stat(FLOATING_ON, &sbuf)) ? _FixedRequestLicense()
: _FloatingRequestLicense();
#else
/*
* FIXED and/or FLOAT (ROLLOVER).
* o) The "root" user name does not require a license.
* o) If the user is already logged onto this device,
* no license is needed.
* o) For non-root users, if a fixed license is available
* then succeed.
* o) Else, if the FLOATING_ON file does exist and a floating
* license exists, then succeed.
* o) If either the FLOATING_ON file does not exist or a floating
* license does not exist, then fail.
*/
if (strcmp(uname, "root") && !_LoggedOn(tty) &&
!_FixedRequestLicense() &&
(stat(FLOATING_ON, &sbuf) || !_FloatingRequestLicense()) )
rc = 0;
#endif /* MUTUALLY_EXCLUSIVE */
fast_exit:
return(rc);
}
/*
* NAME: _ReleaseLicense
*
* FUNCTION: Releases a user license, if one was obtained by this process.
*
* RETURNS: Nothing
*/
void
_ReleaseLicense(void)
{
if (LicenseType == FixedLicense)
_FixedReleaseLicense();
else
if (LicenseType == FloatingLicense)
_FloatingReleaseLicense(getpid());
LicenseType = NoLicense;
}