Blob Blame History Raw
/*********************************************************/
/*  toksvc: a framework-native token broker service      */
/*  Copyright (C) 2020  Z. Gilboa                        */
/*  Released under GPLv2 and GPLv3; see COPYING.TOKSVC.  */
/*********************************************************/

#include <ntapi/ntapi.h>
#include <stdio.h>
#include <unistd.h>

#include <toksvc/toksvc.h>
#include "toksvc_init_impl.h"
#include "toksvc_driver_impl.h"
#include "toksvc_dprintf_impl.h"
#include "toksvc_nolibc_impl.h"
#include "toksvc_log_impl.h"

#ifndef TOKS_DRIVER_FLAGS
#define TOKS_DRIVER_FLAGS	TOKS_DRIVER_VERBOSITY_ERRORS \
				| TOKS_DRIVER_DAEMON_ALWAYS   \
				| TOKS_DRIVER_VERBOSITY_USAGE
#endif

static const char vermsg[] = "%s%s%s (git://midipix.org/toksvc): "
			     "version %s%d.%d.%d%s.\n"
			     "[commit reference: %s%s%s]\n";

static const char * const toks_ver_color[6] = {
		"\x1b[1m\x1b[35m","\x1b[0m",
		"\x1b[1m\x1b[32m","\x1b[0m",
		"\x1b[1m\x1b[34m","\x1b[0m"
};

static const char * const toks_ver_plain[6] = {
		"","",
		"","",
		"",""
};

static ssize_t toks_version(struct toks_driver_ctx * dctx)
{
	const struct toks_source_version * verinfo;
	const char * const * verclr;

	verinfo = toks_source_version();
	verclr  = isatty(STDOUT_FILENO) ? toks_ver_color : toks_ver_plain;

	return toks_dprintf(
			STDOUT_FILENO,vermsg,
			verclr[0],dctx->program,verclr[1],
			verclr[2],verinfo->major,verinfo->minor,
			verinfo->revision,verclr[3],
			verclr[4],verinfo->commit,verclr[5]);
}

static ssize_t toks_output_service_info(const struct toks_service_info * svcinfo)
{
	char guidstr[40];

	toks_uuid_to_string(
		&svcinfo->uuid,
		&guidstr);

	return toks_dprintf(STDOUT_FILENO,
		"toks_uuid:%s\n"
		"toks_syspid:%d\n"
		"toks_systid:%d\n"
		"toks_ctrlpid:%d\n"
		"toks_csyspid:%d\n"
		"toks_allocated:%d\n"
		"toks_available:%d\n"
		"toks_pending:%d\n"
		"toks_tused:%d\n"
		"toks_tfree:%d\n"
		"toks_loglevel:%d\n",
		guidstr,
		svcinfo->syspid,
		svcinfo->systid,
		svcinfo->ctrlpid,
		svcinfo->csyspid,
		svcinfo->allocated,
		svcinfo->available,
		svcinfo->pending,
		svcinfo->tused,
		svcinfo->tfree,
		svcinfo->loglevel);
}

static void toks_output_token_info(const struct toks_token_ctx * tokctx)
{
	int                             idx;
	const struct toks_token_info *  tinfo;
	char                            path[2048];

	toks_dprintf(STDOUT_FILENO,"toks_tokens:{%s",
		tokctx->tused ? "\n" : "");

	for (idx=0,tinfo=tokctx->tokens; idx<tokctx->tused; tinfo++) {
		toks_log_get_arbitrary_process_name(
			&(nt_cid){.process_id=tinfo->tsyspid,.thread_id=0},
			path,sizeof(path));

		toks_dprintf(STDOUT_FILENO,
			"\t{"
			".token_no=%d, "
			".token_id=%08X-%08X-%08X-%08X-%08X-%08X, "
			".token_pid=%d, "
			".token_syspid=%d, "
			".token_refstr=``%s''}\n"
			"\t\t"
			".client_image=%s\n",
			++idx,
			tinfo->keys[0],
			tinfo->keys[1],
			tinfo->keys[2],
			tinfo->keys[3],
			tinfo->keys[4],
			tinfo->keys[5],
			tinfo->tokpid,
			tinfo->tsyspid,
			tinfo->meta,
			path);
	}

	toks_dprintf(STDOUT_FILENO,"}\n");

	toks_dprintf(STDOUT_FILENO,"toks_waiters:{%s",
		tokctx->pending ? "\n" : "");

	for (idx=0; idx<tokctx->pending; tinfo++) {
		toks_log_get_arbitrary_process_name(
			&(nt_cid){.process_id=tinfo->tsyspid,.thread_id=0},
			path,sizeof(path));

		toks_dprintf(STDOUT_FILENO,
			"\t{"
			".waiter_no=%d, "
			".token_id=%08X-%08X-%08X-%08X-%08X-%08X, "
			".token_pid=%d, "
			".token_syspid=%d, "
			".token_refstr=``%s''}\n"
			"\t\t"
			".client_image=%s\n",
			++idx,
			tinfo->keys[0],
			tinfo->keys[1],
			tinfo->keys[2],
			tinfo->keys[3],
			tinfo->keys[4],
			tinfo->keys[5],
			tinfo->tokpid,
			tinfo->tsyspid,
			tinfo->meta,
			path);
	}

	toks_dprintf(STDOUT_FILENO,"}\n");
}

static int toks_exit(struct toks_driver_ctx * dctx, int nerrors)
{
	toks_free_driver_ctx(dctx);
	return nerrors ? 2 : 0;
}

int toks_main(char ** argv, char ** envp)
{
	int				ret;
	int32_t				status;
	struct toks_driver_ctx *	dctx;
	struct toks_token_string	toks;
	struct toks_service_info	svcinfo;
	struct toks_server_snapshot *	snapshot;

	if ((ret = toks_init()))
		return ret;

	if ((ret = toks_get_driver_ctx(argv,envp,TOKS_DRIVER_FLAGS,&dctx)))
		return (ret == TOKS_USAGE)
			? !argv || !argv[0] || !argv[1]
			: 2;

	if (dctx->cctx->drvflags & TOKS_DRIVER_VERSION)
		if ((toks_version(dctx)) < 0)
			return toks_exit(dctx,2);

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_ACQUIRE) {
		ret = (status = toks_client_acquire(dctx))
			? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				break;

			case NT_STATUS_TIMEOUT:
				toks_dprintf(STDERR_FILENO,
					"%s: the operataion timed out [0x%x].\n",
					toks_log_basename(argv[0]),status);
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the operataion has failed [0x%x].\n",
					toks_log_basename(argv[0]),status);
		}

		if (ret == 0) {
			toks_client_token_to_str(dctx,&toks);
			toks_dprintf(STDOUT_FILENO,"%s\n",toks.token);
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_RELEASE) {
		ret = toks_client_release(dctx)
			? 2 : 0;

		if (ret == 0) {
			toks_client_token_to_str(dctx,&toks);
			toks_dprintf(STDOUT_FILENO,"token %s has been released\n",toks.token);
		}

		if (ret == 2) {
			toks_dprintf(STDERR_FILENO,"error: invalid token");
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_NTOKENS_GET) {
		toks_dprintf(STDOUT_FILENO,
			"toks_ntokens:%d\n",
			toks_get_driver_ntokens(dctx));
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_NTOKENS_SET) {
		status = toks_service_ioctl(
			dctx,TOKS_IOCTL_SET_TOKEN_COUNT,
			&(uintptr_t){toks_get_driver_ntokens(dctx)},
			0);

		ret = status ? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				toks_dprintf(STDERR_FILENO,
					"%s: the service's ntokens parameter "
					"was successfully set to %d\n",
					toks_log_basename(argv[0]),
					toks_get_driver_ntokens(dctx));
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the TOKS_IOCTL_SET_TOKEN_COUNT "
					"ioctl operataion has failed "
					"(check the system's documentation) [0x%x].",
					toks_log_basename(argv[0]),status);
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_LOGLEVEL_GET) {
		toks_dprintf(STDOUT_FILENO,
			"toks_loglevel:%d\n",
			dctx->cctx->loglevel);
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_LOGLEVEL_SET) {
		status = toks_service_ioctl(
			dctx,TOKS_IOCTL_SET_LOG_LEVEL,
			&(uintptr_t){dctx->cctx->loglevel},
			0);

		ret = status ? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				toks_dprintf(STDERR_FILENO,
					"%s: the service's log-level parameter "
					"was successfully set to %d\n",
					toks_log_basename(argv[0]),
					dctx->cctx->loglevel);
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the TOKS_IOCTL_SET_LOG_LEVEL "
					"ioctl operataion has failed "
					"(check the system's documentation) [0x%x].",
					toks_log_basename(argv[0]),status);
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_SVCINFO_GET) {
		status = toks_client_query_service(dctx,&svcinfo);
		ret    = status ? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				toks_output_service_info(&svcinfo);
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the TOKS_IOCTL_GET_SERVICE_INFO "
					"ioctl operataion has failed "
					"(check the system's documentation) [0x%x].",
					toks_log_basename(argv[0]),status);
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_SVCINFO_LOG) {
		status = toks_service_ioctl(dctx,TOKS_IOCTL_LOG_SERVICE_INFO,0,0);
		ret    = status ? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				toks_dprintf(STDERR_FILENO,
					"%s: a service info log record "
					"was successfully created\n",
					toks_log_basename(argv[0]));
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the TOKS_IOCTL_LOG_SERVICE_INFO "
					"ioctl operataion has failed "
					"(check the system's documentation) [0x%x].",
					toks_log_basename(argv[0]),status);
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_TOKINFO_GET) {
		status = toks_get_server_snapshot(dctx,&snapshot);
		ret    = status ? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				toks_output_service_info(&snapshot->svcinfo);
				toks_output_token_info(snapshot->tokctx);
				toks_free_server_snapshot(snapshot);
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the TOKS_IOCTL_GET_TOKEN_INFO "
					"ioctl operataion has failed "
					"(check the system's documentation) [0x%x].",
					toks_log_basename(argv[0]),status);
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_TOKINFO_LOG) {
		status = toks_service_ioctl(dctx,TOKS_IOCTL_LOG_TOKEN_INFO,0,0);
		ret    = status ? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				toks_dprintf(STDERR_FILENO,
					"%s: a service info log record, "
					"followed by a info table for currently "
					"allocated tokens, was successfully created\n",
					toks_log_basename(argv[0]));
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the TOKS_IOCTL_LOG_TOKEN_INFO "
					"ioctl operataion has failed "
					"(check the system's documentation) [0x%x].",
					toks_log_basename(argv[0]),status);
		}
	}

	if (dctx->cctx->drvflags & TOKS_DRIVER_ACTION_ABORT) {
		ret = (status = toks_service_abort(dctx))
			? 2 : 0;

		switch (status) {
			case NT_STATUS_SUCCESS:
				toks_dprintf(STDERR_FILENO,
					"%s: the server responded with no error.\n",
					toks_log_basename(argv[0]),status);
				break;

			case NT_STATUS_ACCESS_DENIED:
				toks_dprintf(STDERR_FILENO,
					"%s: the abort operataion timed (access denied) [0x%x].\n",
					toks_log_basename(argv[0]),status);
				break;

			default:
				toks_dprintf(STDERR_FILENO,
					"%s: the abort operataion failed ",
					"(check the system's documentation) [0x%x].",
					toks_log_basename(argv[0]),status);
		}
	}

	return (dctx->cctx->drvflags & TOKS_DRIVER_MODE_SERVER)
		? NT_STATUS_SERVICE_NOTIFICATION
		: toks_exit(dctx,ret);
}