Files
sudo/plugins/sudoers/sssd.c
Todd C. Miller 12f3bdf60e Add wrapper functions for dlopen() et al so that we can support
statically compiling in the sudoers plugin but still allow other
plugins to be loaded.  The new --enable-static-sudoers configure
option will cause the sudoers plugin to be compiled statically into
the sudo binary.  This does not prevent other plugins from being
loaded as per sudo.conf.
2013-11-22 16:35:15 -07:00

1407 lines
38 KiB
C

/*
* Copyright (c) 2003-2013 Todd C. Miller <Todd.Miller@courtesan.com>
* Copyright (c) 2011 Daniel Kopecek <dkopecek@redhat.com>
*
* This code is derived from software contributed by Aaron Spangler.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#ifdef TIME_WITH_SYS_TIME
# include <time.h>
#endif
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <stdint.h>
#include "sudoers.h"
#include "parse.h"
#include "lbuf.h"
#include "sudo_dso.h"
#include "sudo_debug.h"
/* SSSD <--> SUDO interface - do not change */
struct sss_sudo_attr {
char *name;
char **values;
unsigned int num_values;
};
struct sss_sudo_rule {
unsigned int num_attrs;
struct sss_sudo_attr *attrs;
};
struct sss_sudo_result {
unsigned int num_rules;
struct sss_sudo_rule *rules;
};
typedef int (*sss_sudo_send_recv_t)(uid_t, const char*, const char*,
uint32_t*, struct sss_sudo_result**);
typedef int (*sss_sudo_send_recv_defaults_t)(uid_t, const char*, uint32_t*,
char**, struct sss_sudo_result**);
typedef void (*sss_sudo_free_result_t)(struct sss_sudo_result*);
typedef int (*sss_sudo_get_values_t)(struct sss_sudo_rule*, const char*,
char***);
typedef void (*sss_sudo_free_values_t)(char**);
/* sudo_nss implementation */
struct sudo_sss_handle {
char *domainname;
struct passwd *pw;
void *ssslib;
sss_sudo_send_recv_t fn_send_recv;
sss_sudo_send_recv_defaults_t fn_send_recv_defaults;
sss_sudo_free_result_t fn_free_result;
sss_sudo_get_values_t fn_get_values;
sss_sudo_free_values_t fn_free_values;
};
static int sudo_sss_open(struct sudo_nss *nss);
static int sudo_sss_close(struct sudo_nss *nss);
static int sudo_sss_parse(struct sudo_nss *nss);
static void sudo_sss_parse_options(struct sudo_sss_handle *handle,
struct sss_sudo_rule *rule);
static int sudo_sss_setdefs(struct sudo_nss *nss);
static int sudo_sss_lookup(struct sudo_nss *nss, int ret, int pwflag);
static int sudo_sss_display_cmnd(struct sudo_nss *nss, struct passwd *pw);
static int sudo_sss_display_defaults(struct sudo_nss *nss, struct passwd *pw,
struct lbuf *lbuf);
static int sudo_sss_display_bound_defaults(struct sudo_nss *nss,
struct passwd *pw, struct lbuf *lbuf);
static int sudo_sss_display_privs(struct sudo_nss *nss, struct passwd *pw,
struct lbuf *lbuf);
static struct sss_sudo_result *sudo_sss_result_get(struct sudo_nss *nss,
struct passwd *pw,
uint32_t *state);
static void
sudo_sss_attrcpy(struct sss_sudo_attr *dst, const struct sss_sudo_attr *src)
{
int i;
debug_decl(sudo_sss_attrcpy, SUDO_DEBUG_SSSD)
sudo_debug_printf(SUDO_DEBUG_DEBUG, "dst=%p, src=%p", dst, src);
sudo_debug_printf(SUDO_DEBUG_INFO, "emalloc: cnt=%d", src->num_values);
dst->name = estrdup(src->name);
dst->num_values = src->num_values;
dst->values = emalloc2(dst->num_values, sizeof(char *));
for (i = 0; i < dst->num_values; ++i)
dst->values[i] = estrdup(src->values[i]);
debug_return;
}
static void
sudo_sss_rulecpy(struct sss_sudo_rule *dst, const struct sss_sudo_rule *src)
{
int i;
debug_decl(sudo_sss_rulecpy, SUDO_DEBUG_SSSD)
sudo_debug_printf(SUDO_DEBUG_DEBUG, "dst=%p, src=%p", dst, src);
sudo_debug_printf(SUDO_DEBUG_INFO, "emalloc: cnt=%d", src->num_attrs);
dst->num_attrs = src->num_attrs;
dst->attrs = emalloc2(dst->num_attrs, sizeof(struct sss_sudo_attr));
for (i = 0; i < dst->num_attrs; ++i)
sudo_sss_attrcpy(dst->attrs + i, src->attrs + i);
debug_return;
}
#define _SUDO_SSS_FILTER_INCLUDE 0
#define _SUDO_SSS_FILTER_EXCLUDE 1
#define _SUDO_SSS_STATE_HOSTMATCH 0x01
#define _SUDO_SSS_STATE_USERMATCH 0x02
static struct sss_sudo_result *
sudo_sss_filter_result(struct sudo_sss_handle *handle,
struct sss_sudo_result *in_res,
int (*filterp)(struct sudo_sss_handle *, struct sss_sudo_rule *, void *),
int act, void *filterp_arg)
{
struct sss_sudo_result *out_res;
int i, l, r;
debug_decl(sudo_sss_filter_result, SUDO_DEBUG_SSSD)
sudo_debug_printf(SUDO_DEBUG_DEBUG, "in_res=%p, count=%u, act=%s",
in_res, in_res->num_rules,
act == _SUDO_SSS_FILTER_EXCLUDE ? "EXCLUDE" : "INCLUDE");
if (in_res == NULL)
debug_return_ptr(NULL);
sudo_debug_printf(SUDO_DEBUG_DEBUG, "emalloc: cnt=%d", in_res->num_rules);
out_res = emalloc(sizeof(struct sss_sudo_result));
out_res->rules = in_res->num_rules > 0 ?
emalloc2(in_res->num_rules, sizeof(struct sss_sudo_rule)) : NULL;
out_res->num_rules = 0;
for (i = l = 0; i < in_res->num_rules; ++i) {
r = filterp(handle, in_res->rules + i, filterp_arg);
if (( r && act == _SUDO_SSS_FILTER_INCLUDE) ||
(!r && act == _SUDO_SSS_FILTER_EXCLUDE)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"COPY (%s): %p[%u] => %p[%u] (= %p)",
act == _SUDO_SSS_FILTER_EXCLUDE ? "not excluded" : "included",
in_res->rules, i, out_res->rules, l, in_res->rules + i);
sudo_sss_rulecpy(out_res->rules + l, in_res->rules + i);
++l;
}
}
if (l < in_res->num_rules) {
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"reallocating result: %p (count: %u -> %u)", out_res->rules,
in_res->num_rules, l);
if (l > 0) {
out_res->rules =
erealloc3(out_res->rules, l, sizeof(struct sss_sudo_rule));
} else {
efree(out_res->rules);
out_res->rules = NULL;
}
}
out_res->num_rules = l;
debug_return_ptr(out_res);
}
struct sudo_nss sudo_nss_sss = {
{ NULL, NULL },
sudo_sss_open,
sudo_sss_close,
sudo_sss_parse,
sudo_sss_setdefs,
sudo_sss_lookup,
sudo_sss_display_cmnd,
sudo_sss_display_defaults,
sudo_sss_display_bound_defaults,
sudo_sss_display_privs
};
/* sudo_nss implementation */
// ok
static int sudo_sss_open(struct sudo_nss *nss)
{
struct sudo_sss_handle *handle;
static const char path[] = _PATH_SSSD_LIB"/libsss_sudo.so";
debug_decl(sudo_sss_open, SUDO_DEBUG_SSSD);
/* Create a handle container. */
handle = emalloc(sizeof(struct sudo_sss_handle));
/* Load symbols */
handle->ssslib = sudo_dso_load(path, SUDO_DSO_LAZY);
if (handle->ssslib == NULL) {
warningx(U_("unable to load %s: %s"), path, sudo_dso_strerror());
warningx(U_("unable to initialize SSS source. Is SSSD installed on your machine?"));
debug_return_int(EFAULT);
}
handle->fn_send_recv =
sudo_dso_findsym(handle->ssslib, "sss_sudo_send_recv");
if (handle->fn_send_recv == NULL) {
warningx(U_("unable to find symbol \"%s\" in %s"), path,
"sss_sudo_send_recv");
debug_return_int(EFAULT);
}
handle->fn_send_recv_defaults =
sudo_dso_findsym(handle->ssslib, "sss_sudo_send_recv_defaults");
if (handle->fn_send_recv_defaults == NULL) {
warningx(U_("unable to find symbol \"%s\" in %s"), path,
"sss_sudo_send_recv_defaults");
debug_return_int(EFAULT);
}
handle->fn_free_result =
sudo_dso_findsym(handle->ssslib, "sss_sudo_free_result");
if (handle->fn_free_result == NULL) {
warningx(U_("unable to find symbol \"%s\" in %s"), path,
"sss_sudo_free_result");
debug_return_int(EFAULT);
}
handle->fn_get_values =
sudo_dso_findsym(handle->ssslib, "sss_sudo_get_values");
if (handle->fn_get_values == NULL) {
warningx(U_("unable to find symbol \"%s\" in %s"), path,
"sss_sudo_get_values");
debug_return_int(EFAULT);
}
handle->fn_free_values =
sudo_dso_findsym(handle->ssslib, "sss_sudo_free_values");
if (handle->fn_free_values == NULL) {
warningx(U_("unable to find symbol \"%s\" in %s"), path,
"sss_sudo_free_values");
debug_return_int(EFAULT);
}
handle->domainname = NULL;
handle->pw = sudo_user.pw;
nss->handle = handle;
sudo_debug_printf(SUDO_DEBUG_DEBUG, "handle=%p", handle);
debug_return_int(0);
}
// ok
static int sudo_sss_close(struct sudo_nss *nss)
{
struct sudo_sss_handle *handle;
debug_decl(sudo_sss_close, SUDO_DEBUG_SSSD);
if (nss && nss->handle) {
handle = nss->handle;
sudo_dso_unload(handle->ssslib);
}
efree(nss->handle);
debug_return_int(0);
}
// ok
static int sudo_sss_parse(struct sudo_nss *nss)
{
debug_decl(sudo_sss_parse, SUDO_DEBUG_SSSD);
debug_return_int(0);
}
static int sudo_sss_setdefs(struct sudo_nss *nss)
{
struct sudo_sss_handle *handle = nss->handle;
struct sss_sudo_result *sss_result;
struct sss_sudo_rule *sss_rule;
uint32_t sss_error;
int i;
debug_decl(sudo_sss_setdefs, SUDO_DEBUG_SSSD);
if (handle == NULL)
debug_return_int(-1);
sudo_debug_printf(SUDO_DEBUG_DIAG, "Looking for cn=defaults");
if (handle->fn_send_recv_defaults(handle->pw->pw_uid, handle->pw->pw_name,
&sss_error, &handle->domainname,
&sss_result) != 0) {
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_send_recv_defaults: != 0, sss_error=%u", sss_error);
debug_return_int(-1);
}
if (sss_error == ENOENT) {
sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD.");
debug_return_int(0);
} else if(sss_error != 0) {
sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error);
debug_return_int(-1);
}
for (i = 0; i < sss_result->num_rules; ++i) {
sudo_debug_printf(SUDO_DEBUG_DIAG,
"Parsing cn=defaults, %d/%d", i, sss_result->num_rules);
sss_rule = sss_result->rules + i;
sudo_sss_parse_options(handle, sss_rule);
}
handle->fn_free_result(sss_result);
debug_return_int(0);
}
static int sudo_sss_checkpw(struct sudo_nss *nss, struct passwd *pw)
{
struct sudo_sss_handle *handle = nss->handle;
debug_decl(sudo_sss_checkpw, SUDO_DEBUG_SSSD);
if (pw->pw_name != handle->pw->pw_name ||
pw->pw_uid != handle->pw->pw_uid) {
sudo_debug_printf(SUDO_DEBUG_DIAG,
"Requested name or uid don't match the initial once, reinitializing...");
handle->pw = pw;
if (sudo_sss_setdefs(nss) != 0)
debug_return_int(-1);
}
debug_return_int(0);
}
static int
sudo_sss_check_runas_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *sss_rule)
{
char **val_array = NULL;
char *val;
int ret = false, i;
debug_decl(sudo_sss_check_runas_user, SUDO_DEBUG_SSSD);
if (!runas_pw)
debug_return_int(UNSPEC);
/* get the runas user from the entry */
switch (handle->fn_get_values(sss_rule, "sudoRunAsUser", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result. Trying old style (sudoRunAs)");
/* try old style */
switch (handle->fn_get_values(sss_rule, "sudoRunAs", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result. Matching against runas_default");
/*
* If there are no runas entries, match runas_default against
* what the user specified on the command line.
*/
return !strcasecmp(runas_pw->pw_name, def_runas_default);
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAs): != 0");
debug_return_int(UNSPEC);
}
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAsUser): != 0");
debug_return_int(UNSPEC);
}
/*
* BUG:
*
* if runas is not specified on the command line, the only information
* as to which user to run as is in the runas_default option. We should
* check to see if we have the local option present. Unfortunately we
* don't parse these options until after this routine says yes or no.
* The query has already returned, so we could peek at the attribute
* values here though.
*
* For now just require users to always use -u option unless its set
* in the global defaults. This behaviour is no different than the global
* /etc/sudoers.
*
* Sigh - maybe add this feature later
*/
/* walk through values returned, looking for a match */
for (i = 0; val_array[i] != NULL && !ret; ++i) {
val = val_array[i];
sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val);
switch (val[0]) {
case '+':
sudo_debug_printf(SUDO_DEBUG_DEBUG, "netgr_");
if (netgr_matches(val, NULL, NULL, runas_pw->pw_name)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "=> match");
ret = true;
}
break;
case '%':
sudo_debug_printf(SUDO_DEBUG_DEBUG, "usergr_");
if (usergr_matches(val, runas_pw->pw_name, runas_pw)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "=> match");
ret = true;
}
break;
case 'A':
if (strcmp(val, "ALL") == 0) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "ALL => match");
ret = true;
break;
}
/* FALLTHROUGH */
sudo_debug_printf(SUDO_DEBUG_DEBUG, "FALLTHROUGH");
default:
if (userpw_matches(val, runas_pw->pw_name, runas_pw)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"%s == %s (pw_name) => match", val, runas_pw->pw_name);
ret = true;
}
break;
}
sudo_debug_printf(SUDO_DEBUG_INFO,
"sssd/ldap sudoRunAsUser '%s' ... %s", val, ret ? "MATCH!" : "not");
}
handle->fn_free_values(val_array); /* cleanup */
debug_return_int(ret);
}
static int
sudo_sss_check_runas_group(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule)
{
char **val_array = NULL;
char *val;
int ret = false, i;
debug_decl(sudo_sss_check_runas_group, SUDO_DEBUG_SSSD);
/* runas_gr is only set if the user specified the -g flag */
if (!runas_gr)
debug_return_int(UNSPEC);
/* get the values from the entry */
switch (handle->fn_get_values(rule, "sudoRunAsGroup", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
debug_return_int(false);
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_get_values(sudoRunAsGroup): != 0");
debug_return_int(UNSPEC);
}
/* walk through values returned, looking for a match */
for (i = 0; val_array[i] != NULL; ++i) {
val = val_array[i];
sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val);
if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr))
ret = true;
sudo_debug_printf(SUDO_DEBUG_INFO,
"sssd/ldap sudoRunAsGroup '%s' ... %s", val, ret ? "MATCH!" : "not");
}
handle->fn_free_values(val_array);
debug_return_int(ret);
}
/*
* Walk through search results and return true if we have a runas match,
* else false. RunAs info is optional.
*/
static int
sudo_sss_check_runas(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule)
{
int ret;
debug_decl(sudo_sss_check_runas, SUDO_DEBUG_SSSD);
if (rule == NULL)
debug_return_int(false);
ret = sudo_sss_check_runas_user(handle, rule) != false &&
sudo_sss_check_runas_group(handle, rule) != false;
debug_return_int(ret);
}
static int
sudo_sss_check_host(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule)
{
char **val_array, *val;
int ret = false, i;
debug_decl(sudo_sss_check_host, SUDO_DEBUG_SSSD);
if (rule == NULL)
debug_return_int(ret);
/* get the values from the rule */
switch (handle->fn_get_values(rule, "sudoHost", &val_array))
{
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
debug_return_int(false);
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoHost): != 0");
debug_return_int(ret);
}
/* walk through values */
for (i = 0; val_array[i] != NULL; ++i) {
val = val_array[i];
sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val);
/* match any or address or netgroup or hostname */
if (!strcmp(val, "ALL") || addr_matches(val) ||
netgr_matches(val, user_host, user_shost, NULL) ||
hostname_matches(user_shost, user_host, val))
ret = true;
sudo_debug_printf(SUDO_DEBUG_INFO,
"sssd/ldap sudoHost '%s' ... %s", val, ret ? "MATCH!" : "not");
}
handle->fn_free_values(val_array);
debug_return_int(ret);
}
static int
sudo_sss_result_filterp(struct sudo_sss_handle *handle,
struct sss_sudo_rule *rule, void *unused)
{
(void)unused;
debug_decl(sudo_sss_result_filterp, SUDO_DEBUG_SSSD);
if (sudo_sss_check_host(handle, rule))
debug_return_int(1);
else
debug_return_int(0);
}
static struct sss_sudo_result *
sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw, uint32_t *state)
{
struct sudo_sss_handle *handle = nss->handle;
struct sss_sudo_result *u_sss_result, *f_sss_result;
uint32_t sss_error = 0, ret;
debug_decl(sudo_sss_result_get, SUDO_DEBUG_SSSD);
if (sudo_sss_checkpw(nss, pw) != 0)
debug_return_ptr(NULL);
sudo_debug_printf(SUDO_DEBUG_DIAG, " username=%s", handle->pw->pw_name);
sudo_debug_printf(SUDO_DEBUG_DIAG, "domainname=%s", handle->domainname);
u_sss_result = f_sss_result = NULL;
ret = handle->fn_send_recv(handle->pw->pw_uid, handle->pw->pw_name,
handle->domainname, &sss_error, &u_sss_result);
switch (ret) {
case 0:
switch (sss_error) {
case 0:
if (u_sss_result != NULL) {
if (state != NULL) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "state |= USERMATCH");
*state |= _SUDO_SSS_STATE_USERMATCH;
}
sudo_debug_printf(SUDO_DEBUG_INFO, "Received %u rule(s)",
u_sss_result->num_rules);
} else {
sudo_debug_printf(SUDO_DEBUG_INFO,
"Internal error: u_sss_result == NULL && sss_error == 0");
debug_return_ptr(NULL);
}
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD.");
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error);
debug_return_ptr(NULL);
}
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_send_recv: != 0: ret=%d", ret);
debug_return_ptr(NULL);
}
f_sss_result = sudo_sss_filter_result(handle, u_sss_result,
sudo_sss_result_filterp, _SUDO_SSS_FILTER_INCLUDE, NULL);
if (f_sss_result != NULL) {
if (f_sss_result->num_rules > 0) {
if (state != NULL) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "state |= HOSTMATCH");
*state |= _SUDO_SSS_STATE_HOSTMATCH;
}
}
}
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"u_sss_result=(%p, %u) => f_sss_result=(%p, %u)", u_sss_result,
u_sss_result->num_rules, f_sss_result, f_sss_result->num_rules);
handle->fn_free_result(u_sss_result);
debug_return_ptr(f_sss_result);
}
/*
* Search for boolean "option" in sudoOption.
* Returns true if found and allowed, false if negated, else UNSPEC.
*/
static int
sudo_sss_check_bool(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule,
char *option)
{
char ch, *var, **val_array = NULL;
int i, ret = UNSPEC;
debug_decl(sudo_sss_check_bool, SUDO_DEBUG_SSSD);
if (rule == NULL)
debug_return_int(ret);
switch (handle->fn_get_values(rule, "sudoOption", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
debug_return_int(ret);
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0");
debug_return_int(ret);
}
/* walk through options */
for (i = 0; val_array[i] != NULL; ++i) {
var = val_array[i];
sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoOption: '%s'", var);
if ((ch = *var) == '!')
var++;
if (strcmp(var, option) == 0)
ret = (ch != '!');
}
handle->fn_free_values(val_array);
debug_return_int(ret);
}
/*
* If a digest prefix is present, fills in struct sudo_digest
* and returns a pointer to it, updating cmnd to point to the
* command after the digest.
*/
static struct sudo_digest *
sudo_sss_extract_digest(char **cmnd, struct sudo_digest *digest)
{
char *ep, *cp = *cmnd;
int digest_type = SUDO_DIGEST_INVALID;
debug_decl(sudo_sss_check_command, SUDO_DEBUG_LDAP)
/*
* Check for and extract a digest prefix, e.g.
* sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls
*/
if (cp[0] == 's' && cp[1] == 'h' && cp[2] == 'a') {
switch (cp[3]) {
case '2':
if (cp[4] == '2' && cp[5] == '4')
digest_type = SUDO_DIGEST_SHA224;
else if (cp[4] == '5' && cp[5] == '6')
digest_type = SUDO_DIGEST_SHA256;
break;
case '3':
if (cp[4] == '8' && cp[5] == '4')
digest_type = SUDO_DIGEST_SHA384;
break;
case '5':
if (cp[4] == '1' && cp[5] == '2')
digest_type = SUDO_DIGEST_SHA512;
break;
}
if (digest_type != SUDO_DIGEST_INVALID) {
cp += 6;
while (isblank((unsigned char)*cp))
cp++;
if (*cp == ':') {
cp++;
while (isblank((unsigned char)*cp))
cp++;
ep = cp;
while (*ep != '\0' && !isblank((unsigned char)*ep))
ep++;
if (*ep != '\0') {
digest->digest_type = digest_type;
digest->digest_str = estrndup(cp, (size_t)(ep - cp));
cp = ep + 1;
while (isblank((unsigned char)*cp))
cp++;
*cmnd = cp;
sudo_debug_printf(SUDO_DEBUG_INFO,
"%s digest %s for %s",
digest_type == SUDO_DIGEST_SHA224 ? "sha224" :
digest_type == SUDO_DIGEST_SHA256 ? "sha256" :
digest_type == SUDO_DIGEST_SHA384 ? "sha384" :
"sha512", digest->digest_str, cp);
debug_return_ptr(digest);
}
}
}
}
debug_return_ptr(NULL);
}
/*
* Walk through search results and return true if we have a command match,
* false if disallowed and UNSPEC if not matched.
*/
static int
sudo_sss_check_command(struct sudo_sss_handle *handle,
struct sss_sudo_rule *rule, int *setenv_implied)
{
char **val_array = NULL, *val;
char *allowed_cmnd, *allowed_args;
int i, foundbang, ret = UNSPEC;
struct sudo_digest digest, *allowed_digest = NULL;
debug_decl(sudo_sss_check_command, SUDO_DEBUG_SSSD);
if (rule == NULL)
debug_return_int(ret);
switch (handle->fn_get_values(rule, "sudoCommand", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
debug_return_int(ret);
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0");
debug_return_int(ret);
}
for (i = 0; val_array[i] != NULL && ret != false; ++i) {
val = val_array[i];
sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val);
/* Match against ALL ? */
if (!strcmp(val, "ALL")) {
ret = true;
if (setenv_implied != NULL)
*setenv_implied = true;
sudo_debug_printf(SUDO_DEBUG_INFO,
"sssd/ldap sudoCommand '%s' ... MATCH!", val);
continue;
}
/* check for sha-2 digest */
allowed_digest = sudo_sss_extract_digest(&val, &digest);
/* check for !command */
if (*val == '!') {
foundbang = true;
allowed_cmnd = estrdup(1 + val); /* !command */
} else {
foundbang = false;
allowed_cmnd = estrdup(val); /* command */
}
/* split optional args away from command */
allowed_args = strchr(allowed_cmnd, ' ');
if (allowed_args)
*allowed_args++ = '\0';
/* check the command like normal */
if (command_matches(allowed_cmnd, allowed_args, NULL)) {
/*
* If allowed (no bang) set ret but keep on checking.
* If disallowed (bang), exit loop.
*/
ret = foundbang ? false : true;
}
sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoCommand '%s' ... %s",
val, ret == true ? "MATCH!" : "not");
efree(allowed_cmnd); /* cleanup */
}
handle->fn_free_values(val_array); /* more cleanup */
debug_return_int(ret);
}
static void
sudo_sss_parse_options(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule)
{
int i;
char op, *v, *val;
char **val_array = NULL;
debug_decl(sudo_sss_parse_options, SUDO_DEBUG_SSSD);
if (rule == NULL)
debug_return;
switch (handle->fn_get_values(rule, "sudoOption", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
debug_return;
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoOption): != 0");
debug_return;
}
/* walk through options */
for (i = 0; val_array[i] != NULL; i++) {
sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoOption: '%s'",
val_array[i]);
v = estrdup(val_array[i]);
/* check for equals sign past first char */
val = strchr(v, '=');
if (val > v) {
*val++ = '\0'; /* split on = and truncate var */
op = *(val - 2); /* peek for += or -= cases */
if (op == '+' || op == '-') {
*(val - 2) = '\0'; /* found, remove extra char */
/* case var+=val or var-=val */
set_default(v, val, (int) op);
} else {
/* case var=val */
set_default(v, val, true);
}
} else if (*v == '!') {
/* case !var Boolean False */
set_default(v + 1, NULL, false);
} else {
/* case var Boolean True */
set_default(v, NULL, true);
}
efree(v);
}
handle->fn_free_values(val_array);
debug_return;
}
static int
sudo_sss_lookup(struct sudo_nss *nss, int ret, int pwflag)
{
int rc, setenv_implied;
struct sudo_sss_handle *handle = nss->handle;
struct sss_sudo_result *sss_result = NULL;
struct sss_sudo_rule *rule;
uint32_t i, state = 0;
debug_decl(sudo_sss_lookup, SUDO_DEBUG_SSSD);
/* Fetch list of sudoRole entries that match user and host. */
sss_result = sudo_sss_result_get(nss, sudo_user.pw, &state);
/*
* The following queries are only determine whether or not a
* password is required, so the order of the entries doesn't matter.
*/
if (pwflag) {
int doauth = UNSPEC;
int matched = UNSPEC;
enum def_tuple pwcheck =
(pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
sudo_debug_printf(SUDO_DEBUG_INFO, "perform search for pwflag %d", pwflag);
if (sss_result != NULL) {
for (i = 0; i < sss_result->num_rules; i++) {
rule = sss_result->rules + i;
if ((pwcheck == any && doauth != false) ||
(pwcheck == all && doauth == false)) {
doauth = sudo_sss_check_bool(handle, rule, "authenticate");
}
/* Only check the command when listing another user. */
if (user_uid == 0 || list_pw == NULL ||
user_uid == list_pw->pw_uid ||
sudo_sss_check_command(handle, rule, NULL)) {
matched = true;
break;
}
}
}
if (matched || user_uid == 0) {
SET(ret, VALIDATE_OK);
CLR(ret, VALIDATE_NOT_OK);
if (def_authenticate) {
switch (pwcheck) {
case always:
SET(ret, FLAG_CHECK_USER);
break;
case all:
case any:
if (doauth == false)
def_authenticate = false;
break;
case never:
def_authenticate = false;
break;
default:
break;
}
}
}
goto done;
}
sudo_debug_printf(SUDO_DEBUG_DIAG,
"searching SSSD/LDAP for sudoers entries");
setenv_implied = false;
if (sss_result != NULL) {
for (i = 0; i < sss_result->num_rules; i++) {
rule = sss_result->rules + i;
if (!sudo_sss_check_runas(handle, rule))
continue;
rc = sudo_sss_check_command(handle, rule, &setenv_implied);
if (rc != UNSPEC) {
/* We have a match. */
sudo_debug_printf(SUDO_DEBUG_DIAG, "Command %sallowed",
rc == true ? "" : "NOT ");
if (rc == true) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "SSSD rule: %p", rule);
/* Apply entry-specific options. */
if (setenv_implied)
def_setenv = true;
sudo_sss_parse_options(handle, rule);
#ifdef HAVE_SELINUX
/* Set role and type if not specified on command line. */
if (user_role == NULL)
user_role = def_role;
if (user_type == NULL)
user_type = def_type;
#endif /* HAVE_SELINUX */
SET(ret, VALIDATE_OK);
CLR(ret, VALIDATE_NOT_OK);
} else {
SET(ret, VALIDATE_NOT_OK);
CLR(ret, VALIDATE_OK);
}
break;
}
}
}
done:
sudo_debug_printf(SUDO_DEBUG_DIAG, "Done with LDAP searches");
if (!ISSET(ret, VALIDATE_OK)) {
/* No matching entries. */
if (pwflag && list_pw == NULL)
SET(ret, FLAG_NO_CHECK);
}
if (state & _SUDO_SSS_STATE_USERMATCH)
CLR(ret, FLAG_NO_USER);
if (state & _SUDO_SSS_STATE_HOSTMATCH)
CLR(ret, FLAG_NO_HOST);
sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_sss_lookup(%d)=0x%02x",
pwflag, ret);
debug_return_int(ret);
}
static int
sudo_sss_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
{
struct sudo_sss_handle *handle = nss->handle;
struct sss_sudo_result *sss_result = NULL;
struct sss_sudo_rule *rule;
int i, found = false;
debug_decl(sudo_sss_display_cmnd, SUDO_DEBUG_SSSD);
if (handle == NULL)
goto done;
if (sudo_sss_checkpw(nss, pw) != 0)
debug_return_int(-1);
/*
* The sudo_sss_result_get() function returns all nodes that match
* the user and the host.
*/
sudo_debug_printf(SUDO_DEBUG_DIAG, "sssd/ldap search for command list");
sss_result = sudo_sss_result_get(nss, pw, NULL);
if (sss_result == NULL)
goto done;
for (i = 0; i < sss_result->num_rules; i++) {
rule = sss_result->rules + i;
if (sudo_sss_check_command(handle, rule, NULL) &&
sudo_sss_check_runas(handle, rule)) {
found = true;
goto done;
}
}
done:
if (found)
printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd,
user_args ? " " : "", user_args ? user_args : "");
if (sss_result != NULL)
handle->fn_free_result(sss_result);
debug_return_int(!found);
}
static int
sudo_sss_display_defaults(struct sudo_nss *nss, struct passwd *pw,
struct lbuf *lbuf)
{
struct sudo_sss_handle *handle = nss->handle;
struct sss_sudo_rule *rule;
struct sss_sudo_result *sss_result = NULL;
uint32_t sss_error = 0;
char *prefix, *val, **val_array = NULL;
int count = 0, i, j;
debug_decl(sudo_sss_display_defaults, SUDO_DEBUG_SSSD);
if (handle == NULL)
goto done;
if (handle->fn_send_recv_defaults(pw->pw_uid, pw->pw_name,
&sss_error, &handle->domainname,
&sss_result) != 0) {
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_send_recv_defaults: !=0, sss_error=%u", sss_error);
goto done;
}
if (sss_error == ENOENT) {
sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD.");
goto done;
} else if(sss_error != 0) {
sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error);
goto done;
}
handle->pw = pw;
for (i = 0; i < sss_result->num_rules; ++i) {
rule = sss_result->rules + i;
switch (handle->fn_get_values(rule, "sudoOption", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
continue;
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0");
continue;
}
if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
prefix = " ";
else
prefix = ", ";
for (j = 0; val_array[j] != NULL; ++j) {
val = val_array[j];
lbuf_append(lbuf, "%s%s", prefix, val);
prefix = ", ";
count++;
}
handle->fn_free_values(val_array);
val_array = NULL;
}
handle->fn_free_result(sss_result);
done:
debug_return_int(count);
}
// ok
static int
sudo_sss_display_bound_defaults(struct sudo_nss *nss,
struct passwd *pw, struct lbuf *lbuf)
{
debug_decl(sudo_sss_display_bound_defaults, SUDO_DEBUG_SSSD);
debug_return_int(0);
}
static int
sudo_sss_display_entry_long(struct sudo_sss_handle *handle,
struct sss_sudo_rule *rule, struct lbuf *lbuf)
{
char **val_array = NULL;
int count = 0, i;
debug_decl(sudo_sss_display_entry_long, SUDO_DEBUG_SSSD);
/* get the RunAsUser Values from the entry */
lbuf_append(lbuf, " RunAsUsers: ");
switch (handle->fn_get_values(rule, "sudoRunAsUser", &val_array)) {
case 0:
for (i = 0; val_array[i] != NULL; ++i)
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
handle->fn_free_values(val_array);
break;
case ENOENT:
switch (handle->fn_get_values(rule, "sudoRunAs", &val_array)) {
case 0:
for (i = 0; val_array[i] != NULL; ++i)
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
handle->fn_free_values(val_array);
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
lbuf_append(lbuf, "%s", def_runas_default);
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAs): != 0");
debug_return_int(count);
}
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAsUser): != 0");
debug_return_int(count);
}
lbuf_append(lbuf, "\n");
/* get the RunAsGroup Values from the entry */
switch (handle->fn_get_values(rule, "sudoRunAsGroup", &val_array)) {
case 0:
lbuf_append(lbuf, " RunAsGroups: ");
for (i = 0; val_array[i] != NULL; ++i)
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
handle->fn_free_values(val_array);
lbuf_append(lbuf, "\n");
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_get_values(sudoRunAsGroup): != 0");
debug_return_int(count);
}
/* get the Option Values from the entry */
switch (handle->fn_get_values(rule, "sudoOption", &val_array)) {
case 0:
lbuf_append(lbuf, " Options: ");
for (i = 0; val_array[i] != NULL; ++i)
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
handle->fn_free_values(val_array);
lbuf_append(lbuf, "\n");
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoOption): != 0");
debug_return_int(count);
}
/* Get the command values from the entry. */
switch (handle->fn_get_values(rule, "sudoCommand", &val_array)) {
case 0:
lbuf_append(lbuf, _(" Commands:\n"));
for (i = 0; val_array[i] != NULL; ++i) {
lbuf_append(lbuf, "\t%s\n", val_array[i]);
count++;
}
handle->fn_free_values(val_array);
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_get_values(sudoCommand): != 0");
debug_return_int(count);
}
debug_return_int(count);
}
static int
sudo_sss_display_entry_short(struct sudo_sss_handle *handle,
struct sss_sudo_rule *rule, struct lbuf *lbuf)
{
char **val_array = NULL;
int count = 0, i;
debug_decl(sudo_sss_display_entry_short, SUDO_DEBUG_SSSD);
lbuf_append(lbuf, " (");
/* get the RunAsUser Values from the entry */
switch (handle->fn_get_values(rule, "sudoRunAsUser", &val_array)) {
case 0:
for (i = 0; val_array[i] != NULL; ++i)
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
handle->fn_free_values(val_array);
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result. Trying old style (sudoRunAs).");
/* try old style */
switch (handle->fn_get_values(rule, "sudoRunAs", &val_array)) {
case 0:
for (i = 0; val_array[i] != NULL; ++i)
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
handle->fn_free_values(val_array);
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
lbuf_append(lbuf, "%s", def_runas_default);
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_get_values(sudoRunAs): != 0");
debug_return_int(count);
}
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_get_values(sudoRunAsUser): != 0");
debug_return_int(count);
}
/* get the RunAsGroup Values from the entry */
switch (handle->fn_get_values(rule, "sudoRunAsGroup", &val_array)) {
case 0:
lbuf_append(lbuf, " : ");
for (i = 0; val_array[i] != NULL; ++i)
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
handle->fn_free_values(val_array);
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAsGroup): != 0");
debug_return_int(count);
}
lbuf_append(lbuf, ") ");
/* get the Option Values from the entry */
switch (handle->fn_get_values(rule, "sudoOption", &val_array)) {
case 0:
for (i = 0; val_array[i] != NULL; ++i) {
char *cp = val_array[i];
if (*cp == '!')
cp++;
if (strcmp(cp, "authenticate") == 0)
lbuf_append(lbuf, val_array[i][0] == '!' ?
"NOPASSWD: " : "PASSWD: ");
else if (strcmp(cp, "noexec") == 0)
lbuf_append(lbuf, val_array[i][0] == '!' ?
"EXEC: " : "NOEXEC: ");
else if (strcmp(cp, "setenv") == 0)
lbuf_append(lbuf, val_array[i][0] == '!' ?
"NOSETENV: " : "SETENV: ");
}
handle->fn_free_values(val_array);
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_get_values(sudoOption): != 0");
debug_return_int(count);
}
/* get the Command Values from the entry */
switch (handle->fn_get_values(rule, "sudoCommand", &val_array)) {
case 0:
for (i = 0; val_array[i] != NULL; ++i) {
lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]);
count++;
}
handle->fn_free_values(val_array);
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_INFO, "No result.");
break;
default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"handle->fn_get_values(sudoCommand): != 0");
debug_return_int(count);
}
lbuf_append(lbuf, "\n");
debug_return_int(count);
}
static int
sudo_sss_display_privs(struct sudo_nss *nss, struct passwd *pw,
struct lbuf *lbuf)
{
struct sudo_sss_handle *handle = nss->handle;
struct sss_sudo_result *sss_result = NULL;
struct sss_sudo_rule *rule;
unsigned int i, count = 0;
debug_decl(sudo_sss_display_privs, SUDO_DEBUG_SSSD);
if (handle == NULL)
debug_return_int(-1);
if (sudo_sss_checkpw(nss, pw) != 0)
debug_return_int(-1);
sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap search for command list");
sss_result = sudo_sss_result_get(nss, pw, NULL);
if (sss_result == NULL)
debug_return_int(count);
/* Display all matching entries. */
for (i = 0; i < sss_result->num_rules; ++i) {
rule = sss_result->rules + i;
if (long_list)
count += sudo_sss_display_entry_long(handle, rule, lbuf);
else
count += sudo_sss_display_entry_short(handle, rule, lbuf);
}
if (sss_result != NULL)
handle->fn_free_result(sss_result);
debug_return_int(count);
}