#include "config.h"
#include <stdlib.h>
#include <sys/types.h>
#include <pwd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <dlfcn.h>
#include <vas.h>
#include "compat.h"
#include "logging.h"
#include "nonunix.h"
#include "sudo.h"
#include "parse.h"
#undef TRUE
#undef FALSE
#define FALSE 0
#define TRUE 1
static vas_ctx_t *sudo_vas_ctx;
static vas_id_t *sudo_vas_id;
static const int update_flags = 0;
static int sudo_vas_available = 0;
static char *err_msg = NULL;
static void *libvas_handle = NULL;
static vas_err_t (*v_ctx_alloc) (vas_ctx_t **ctx);
static void (*v_ctx_free) (vas_ctx_t *ctx);
static vas_err_t (*v_id_alloc) (vas_ctx_t *ctx, const char *name, vas_id_t **id);
static void (*v_id_free) (vas_ctx_t *ctx, vas_id_t *id);
static vas_err_t (*v_id_establish_cred_keytab) (vas_ctx_t *ctx, vas_id_t *id, int credflags, const char *keytab);
static vas_err_t (*v_user_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_user_t **user);
static void (*v_user_free) (vas_ctx_t *ctx, vas_user_t *user);
static vas_err_t (*v_group_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_group_t **group);
static void (*v_group_free) (vas_ctx_t *ctx, vas_group_t *group);
static vas_err_t (*v_user_is_member) (vas_ctx_t *ctx, vas_id_t *id, vas_user_t *user, vas_group_t *group);
static const char* (*v_err_get_string) (vas_ctx_t *ctx, int with_cause);
static int resolve_vas_funcs(void);
int
sudo_nonunix_groupcheck_available(void)
{
return sudo_vas_available;
}
int
sudo_nonunix_groupcheck( const char* group, const char* user, const struct passwd* pwd )
{
static int error_cause_shown = FALSE;
int rval = FALSE;
vas_err_t vaserr;
vas_user_t* vas_user = NULL;
vas_group_t* vas_group = NULL;
if (!sudo_vas_available) {
if (error_cause_shown == FALSE) {
warningx("Non-unix group checking unavailable: %s",
err_msg ? err_msg
: "(unknown cause)");
error_cause_shown = TRUE;
}
return 0;
}
if( (vaserr = v_user_init( sudo_vas_ctx, sudo_vas_id, user, update_flags, &vas_user )) != VAS_ERR_SUCCESS ) {
if (vaserr == VAS_ERR_NOT_FOUND) {
vaserr = VAS_ERR_SUCCESS;
}
goto FINISHED;
}
if( (vaserr = v_group_init( sudo_vas_ctx, sudo_vas_id, group, update_flags, &vas_group )) != VAS_ERR_SUCCESS ) {
goto FINISHED;
}
if( (vaserr = v_user_is_member( sudo_vas_ctx, sudo_vas_id, vas_user, vas_group )) == VAS_ERR_SUCCESS ) {
rval = TRUE;
}
else if (vaserr == VAS_ERR_NOT_FOUND) {
vaserr = VAS_ERR_SUCCESS;
}
FINISHED:
if (vaserr != VAS_ERR_SUCCESS && vaserr != VAS_ERR_NOT_FOUND ) {
warningx("Error while checking group membership "
"for user \"%s\", group \"%s\", error: %s%s.", user, group,
v_err_get_string(sudo_vas_ctx, 1),
(strchr(group, '@') && !strchr(group, '.'))
? "\nMake sure the fully qualified domain name is specified"
: "");
}
if( vas_group ) v_group_free( sudo_vas_ctx, vas_group );
if( vas_user ) v_user_free( sudo_vas_ctx, vas_user );
return(rval);
}
static void
set_err_msg(const char *msg, ...) {
va_list ap;
if (!msg)
return;
if (err_msg)
free(err_msg);
va_start(ap, msg);
if (vasprintf(&err_msg, msg, ap) == -1)
err_msg = NULL;
va_end(ap);
}
void
sudo_nonunix_groupcheck_init(void)
{
vas_err_t vaserr;
void *libvas;
if (err_msg) {
free(err_msg);
err_msg = NULL;
}
libvas = dlopen(LIBVAS_SO, RTLD_LAZY);
if (!libvas) {
set_err_msg("dlopen() failed: %s", dlerror());
return;
}
libvas_handle = libvas;
if (resolve_vas_funcs() != 0)
return;
if (VAS_ERR_SUCCESS == (vaserr = v_ctx_alloc(&sudo_vas_ctx))) {
if (VAS_ERR_SUCCESS == (vaserr = v_id_alloc(sudo_vas_ctx, "host/", &sudo_vas_id))) {
if (update_flags & VAS_NAME_FLAG_NO_LDAP) {
sudo_vas_available = 1;
return;
} else {
if ((vaserr = v_id_establish_cred_keytab( sudo_vas_ctx,
sudo_vas_id,
VAS_ID_FLAG_USE_MEMORY_CCACHE
| VAS_ID_FLAG_KEEP_COPY_OF_CRED
| VAS_ID_FLAG_NO_INITIAL_TGT,
NULL )) == VAS_ERR_SUCCESS) {
sudo_vas_available = 1;
return;
}
if (!err_msg)
set_err_msg("unable to establish creds: %s",
v_err_get_string(sudo_vas_ctx, 1));
}
v_id_free(sudo_vas_ctx, sudo_vas_id);
sudo_vas_id = NULL;
}
if (!err_msg)
set_err_msg("Error initializing non-unix group checking: %s",
v_err_get_string(sudo_vas_ctx, 1));
v_ctx_free(sudo_vas_ctx);
sudo_vas_ctx = NULL;
}
if (!err_msg)
set_err_msg("Failed to get a libvas handle for non-unix group checking (unknown cause)");
sudo_vas_available = 0;
}
void
sudo_nonunix_groupcheck_cleanup()
{
if (err_msg) {
free(err_msg);
err_msg = NULL;
}
if (sudo_vas_available) {
v_id_free(sudo_vas_ctx, sudo_vas_id);
sudo_vas_id = NULL;
v_ctx_free(sudo_vas_ctx);
sudo_vas_ctx = NULL;
sudo_vas_available = FALSE;
}
if (libvas_handle) {
if (dlclose(libvas_handle) != 0)
warningx("dlclose() failed: %s", dlerror());
libvas_handle = NULL;
}
}
#define RESOLVE_OR_ERR(fptr, sym) \
do { \
void *_fptr = dlsym(libvas_handle, (sym)); \
if (!_fptr) { \
set_err_msg("dlsym() failed: %s", dlerror()); \
return -1; \
} \
fptr = _fptr; \
} while (0)
int
resolve_vas_funcs(void)
{
if (!libvas_handle)
return -1;
RESOLVE_OR_ERR(v_ctx_alloc, "vas_ctx_alloc");
RESOLVE_OR_ERR(v_ctx_free, "vas_ctx_free");
RESOLVE_OR_ERR(v_id_alloc, "vas_id_alloc");
RESOLVE_OR_ERR(v_id_free, "vas_id_free");
RESOLVE_OR_ERR(v_id_establish_cred_keytab, "vas_id_establish_cred_keytab");
RESOLVE_OR_ERR(v_user_init, "vas_user_init");
RESOLVE_OR_ERR(v_user_free, "vas_user_free");
RESOLVE_OR_ERR(v_group_init, "vas_group_init");
RESOLVE_OR_ERR(v_group_free, "vas_group_free");
RESOLVE_OR_ERR(v_user_is_member, "vas_user_is_member");
RESOLVE_OR_ERR(v_err_get_string, "vas_err_get_string");
return 0;
}