version 1.8, 2008/07/31 16:44:03 |
version 1.9, 2008/11/14 11:58:08 |
|
|
# include <unistd.h> |
# include <unistd.h> |
#endif /* HAVE_UNISTD_H */ |
#endif /* HAVE_UNISTD_H */ |
#include <ctype.h> |
#include <ctype.h> |
#include <limits.h> |
|
#include <pwd.h> |
#include <pwd.h> |
#include <grp.h> |
#include <grp.h> |
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <arpa/inet.h> |
#include <arpa/inet.h> |
#include <netdb.h> |
#include <netdb.h> |
#ifdef HAVE_ERR_H |
|
# include <err.h> |
|
#else |
|
# include "emul/err.h" |
|
#endif /* HAVE_ERR_H */ |
|
#include <errno.h> |
|
#ifdef HAVE_LBER_H |
#ifdef HAVE_LBER_H |
# include <lber.h> |
# include <lber.h> |
#endif |
#endif |
|
|
#elif defined(HAVE_MPS_LDAP_SSL_H) |
#elif defined(HAVE_MPS_LDAP_SSL_H) |
# include <mps/ldap_ssl.h> |
# include <mps/ldap_ssl.h> |
#endif |
#endif |
|
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S |
|
# ifdef HAVE_SASL_SASL_H |
|
# include <sasl/sasl.h> |
|
# else |
|
# include <sasl.h> |
|
# endif |
|
# if HAVE_GSS_KRB5_CCACHE_NAME |
|
# if defined(HAVE_GSSAPI_GSSAPI_KRB5_H) |
|
# include <gssapi/gssapi.h> |
|
# include <gssapi/gssapi_krb5.h> |
|
# elif defined(HAVE_GSSAPI_GSSAPI_H) |
|
# include <gssapi/gssapi.h> |
|
# else |
|
# include <gssapi.h> |
|
# endif |
|
# endif |
|
#endif |
|
|
#include "sudo.h" |
#include "sudo.h" |
#include "parse.h" |
#include "parse.h" |
|
#include "lbuf.h" |
|
|
#ifndef lint |
#ifndef lint |
__unused static const char rcsid[] = "$Sudo: ldap.c,v 1.11.2.38 2008/04/11 14:03:51 millert Exp $"; |
__unused static const char rcsid[] = "$Sudo: ldap.c,v 1.100 2008/04/23 12:30:07 millert Exp $"; |
#endif /* lint */ |
#endif /* lint */ |
|
|
#ifndef LINE_MAX |
|
# define LINE_MAX 2048 |
|
#endif |
|
|
|
#ifndef LDAP_OPT_SUCCESS |
#ifndef LDAP_OPT_SUCCESS |
# define LDAP_OPT_SUCCESS LDAP_SUCCESS |
# define LDAP_OPT_SUCCESS LDAP_SUCCESS |
#endif |
#endif |
|
|
# define LDAPS_PORT 636 |
# define LDAPS_PORT 636 |
#endif |
#endif |
|
|
#define DPRINTF(args, level) if (ldap_conf.debug >= level) warnx args |
#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && !defined(LDAP_SASL_QUIET) |
|
# define LDAP_SASL_QUIET 0 |
|
#endif |
|
|
|
#ifndef HAVE_LDAP_UNBIND_EXT_S |
|
#define ldap_unbind_ext_s(a, b, c) ldap_unbind_s(a) |
|
#endif |
|
|
|
#ifndef HAVE_LDAP_SEARCH_EXT_S |
|
#define ldap_search_ext_s(a, b, c, d, e, f, g, h, i, j, k) \ |
|
ldap_search_s(a, b, c, d, e, f, k) |
|
#endif |
|
|
|
#define LDAP_FOREACH(var, ld, res) \ |
|
for ((var) = ldap_first_entry((ld), (res)); \ |
|
(var) != NULL; \ |
|
(var) = ldap_next_entry((ld), (var))) |
|
|
|
#define DPRINTF(args, level) if (ldap_conf.debug >= level) warningx args |
|
|
#define CONF_BOOL 0 |
#define CONF_BOOL 0 |
#define CONF_INT 1 |
#define CONF_INT 1 |
#define CONF_STR 2 |
#define CONF_STR 2 |
|
|
}; |
}; |
|
|
/* ldap configuration structure */ |
/* ldap configuration structure */ |
struct ldap_config { |
static struct ldap_config { |
int port; |
int port; |
int version; |
int version; |
int debug; |
int debug; |
|
|
int tls_checkpeer; |
int tls_checkpeer; |
int timelimit; |
int timelimit; |
int bind_timelimit; |
int bind_timelimit; |
|
int use_sasl; |
|
int rootuse_sasl; |
int ssl_mode; |
int ssl_mode; |
char *host; |
char *host; |
char *uri; |
char *uri; |
|
|
char *tls_cipher_suite; |
char *tls_cipher_suite; |
char *tls_certfile; |
char *tls_certfile; |
char *tls_keyfile; |
char *tls_keyfile; |
|
char *sasl_auth_id; |
|
char *rootsasl_auth_id; |
|
char *sasl_secprops; |
|
char *krb5_ccname; |
} ldap_conf; |
} ldap_conf; |
|
|
struct ldap_config_table ldap_conf_table[] = { |
static struct ldap_config_table ldap_conf_table[] = { |
{ "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug }, |
{ "sudoers_debug", CONF_INT, FALSE, -1, &ldap_conf.debug }, |
{ "host", CONF_STR, FALSE, -1, &ldap_conf.host }, |
{ "host", CONF_STR, FALSE, -1, &ldap_conf.host }, |
{ "port", CONF_INT, FALSE, -1, &ldap_conf.port }, |
{ "port", CONF_INT, FALSE, -1, &ldap_conf.port }, |
|
|
{ "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw }, |
{ "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw }, |
{ "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn }, |
{ "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn }, |
{ "sudoers_base", CONF_STR, FALSE, -1, &ldap_conf.base }, |
{ "sudoers_base", CONF_STR, FALSE, -1, &ldap_conf.base }, |
|
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S |
|
{ "use_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.use_sasl }, |
|
{ "sasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.sasl_auth_id }, |
|
{ "rootuse_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.rootuse_sasl }, |
|
{ "rootsasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.rootsasl_auth_id }, |
|
# ifdef LDAP_OPT_X_SASL_SECPROPS |
|
{ "sasl_secprops", CONF_STR, TRUE, LDAP_OPT_X_SASL_SECPROPS, |
|
&ldap_conf.sasl_secprops }, |
|
# endif |
|
{ "krb5_ccname", CONF_STR, FALSE, -1, &ldap_conf.krb5_ccname }, |
|
#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */ |
{ NULL } |
{ NULL } |
}; |
}; |
|
|
static void sudo_ldap_update_defaults __P((LDAP *)); |
struct sudo_nss sudo_nss_ldap = { |
static void sudo_ldap_close __P((LDAP *)); |
&sudo_nss_ldap, |
static LDAP *sudo_ldap_open __P((void)); |
NULL, |
|
sudo_ldap_open, |
|
sudo_ldap_close, |
|
sudo_ldap_parse, |
|
sudo_ldap_setdefs, |
|
sudo_ldap_lookup, |
|
sudo_ldap_display_cmnd, |
|
sudo_ldap_display_defaults, |
|
sudo_ldap_display_bound_defaults, |
|
sudo_ldap_display_privs |
|
}; |
|
|
|
#ifdef HAVE_LDAP_CREATE |
|
/* |
|
* Rebuild the hosts list and include a specific port for each host. |
|
* ldap_create() does not take a default port parameter so we must |
|
* append one if we want something other than LDAP_PORT. |
|
*/ |
|
static void |
|
sudo_ldap_conf_add_ports() |
|
{ |
|
|
|
char *host, *port, defport[13]; |
|
char hostbuf[LINE_MAX * 2]; |
|
|
|
hostbuf[0] = '\0'; |
|
if (snprintf(defport, sizeof(defport), ":%d", ldap_conf.port) >= sizeof(defport)) |
|
errorx(1, "sudo_ldap_conf_add_ports: port too large"); |
|
|
|
for ((host = strtok(ldap_conf.host, " \t")); host; (host = strtok(NULL, " \t"))) { |
|
if (hostbuf[0] != '\0') { |
|
if (strlcat(hostbuf, " ", sizeof(hostbuf)) >= sizeof(hostbuf)) |
|
goto toobig; |
|
} |
|
|
|
if (strlcat(hostbuf, host, sizeof(hostbuf)) >= sizeof(hostbuf)) |
|
goto toobig; |
|
/* Append port if there is not one already. */ |
|
if ((port = strrchr(host, ':')) == NULL || !isdigit(port[1])) { |
|
if (strlcat(hostbuf, defport, sizeof(hostbuf)) >= sizeof(hostbuf)) |
|
goto toobig; |
|
} |
|
} |
|
|
|
free(ldap_conf.host); |
|
ldap_conf.host = estrdup(hostbuf); |
|
return; |
|
|
|
toobig: |
|
errorx(1, "sudo_ldap_conf_add_ports: out of space expanding hostbuf"); |
|
} |
|
#endif |
|
|
#ifndef HAVE_LDAP_INITIALIZE |
#ifndef HAVE_LDAP_INITIALIZE |
/* |
/* |
* For each uri, convert to host:port pairs. For ldaps:// enable SSL |
* For each uri, convert to host:port pairs. For ldaps:// enable SSL |
|
|
nldaps++; |
nldaps++; |
host = uri + 8; |
host = uri + 8; |
} else { |
} else { |
warnx("unsupported LDAP uri type: %s", uri); |
warningx("unsupported LDAP uri type: %s", uri); |
goto done; |
goto done; |
} |
} |
|
|
|
|
} |
} |
} |
} |
if (hostbuf[0] == '\0') { |
if (hostbuf[0] == '\0') { |
warnx("invalid uri: %s", uri_list); |
warningx("invalid uri: %s", uri_list); |
goto done; |
goto done; |
} |
} |
|
|
if (nldaps != 0) { |
if (nldaps != 0) { |
if (nldap != 0) { |
if (nldap != 0) { |
warnx("cannot mix ldap and ldaps URIs"); |
warningx("cannot mix ldap and ldaps URIs"); |
goto done; |
goto done; |
} |
} |
if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) { |
if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) { |
warnx("cannot mix ldaps and starttls"); |
warningx("cannot mix ldaps and starttls"); |
goto done; |
goto done; |
} |
} |
ldap_conf.ssl_mode = SUDO_LDAP_SSL; |
ldap_conf.ssl_mode = SUDO_LDAP_SSL; |
|
|
return(rc); |
return(rc); |
|
|
toobig: |
toobig: |
errx(1, "sudo_ldap_parse_uri: out of space building hostbuf"); |
errorx(1, "sudo_ldap_parse_uri: out of space building hostbuf"); |
} |
} |
#endif /* HAVE_LDAP_INITIALIZE */ |
#endif /* HAVE_LDAP_INITIALIZE */ |
|
|
|
|
rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL, |
rc = ldapssl_clientauth_init(ldap_conf.tls_certfile, NULL, |
ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL); |
ldap_conf.tls_keyfile != NULL, ldap_conf.tls_keyfile, NULL); |
if (rc != LDAP_SUCCESS) { |
if (rc != LDAP_SUCCESS) { |
warnx("unable to initialize SSL cert and key db: %s", |
warningx("unable to initialize SSL cert and key db: %s", |
ldapssl_err2string(rc)); |
ldapssl_err2string(rc)); |
goto done; |
goto done; |
} |
} |
|
|
DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2); |
DPRINTF(("ldapssl_init(%s, %d, 1)", host, port), 2); |
if ((ld = ldapssl_init(host, port, 1)) == NULL) |
if ((ld = ldapssl_init(host, port, 1)) != NULL) |
goto done; |
rc = LDAP_SUCCESS; |
} else |
} else |
#endif |
#endif |
{ |
{ |
DPRINTF(("ldap_init(%s, %d)", host, port), 2); |
#ifdef HAVE_LDAP_CREATE |
if ((ld = ldap_init(host, port)) == NULL) |
DPRINTF(("ldap_create()"), 2); |
|
if ((rc = ldap_create(&ld)) != LDAP_SUCCESS) |
goto done; |
goto done; |
|
DPRINTF(("ldap_set_option(LDAP_OPT_HOST_NAME, %s)", host), 2); |
|
rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, host); |
|
#else |
|
DPRINTF(("ldap_init(%s, %d)", host, port), 2); |
|
if ((ld = ldap_init(host, port)) != NULL) |
|
rc = LDAP_SUCCESS; |
|
#endif |
} |
} |
rc = LDAP_SUCCESS; |
|
|
|
done: |
done: |
*ldp = ld; |
*ldp = ld; |
|
|
* netgroup, else FALSE. |
* netgroup, else FALSE. |
*/ |
*/ |
int |
int |
sudo_ldap_check_user_netgroup(ld, entry) |
sudo_ldap_check_user_netgroup(ld, entry, user) |
LDAP *ld; |
LDAP *ld; |
LDAPMessage *entry; |
LDAPMessage *entry; |
|
char *user; |
{ |
{ |
char **v = NULL, **p = NULL; |
struct berval **bv, **p; |
|
char *val; |
int ret = FALSE; |
int ret = FALSE; |
|
|
if (!entry) |
if (!entry) |
return(ret); |
return(ret); |
|
|
/* get the values from the entry */ |
/* get the values from the entry */ |
v = ldap_get_values(ld, entry, "sudoUser"); |
bv = ldap_get_values_len(ld, entry, "sudoUser"); |
|
if (bv == NULL) |
|
return(ret); |
|
|
/* walk through values */ |
/* walk through values */ |
for (p = v; p && *p && !ret; p++) { |
for (p = bv; *p != NULL && !ret; p++) { |
|
val = (*p)->bv_val; |
/* match any */ |
/* match any */ |
if (netgr_matches(*p, NULL, NULL, user_name)) |
if (netgr_matches(val, NULL, NULL, user)) |
ret = TRUE; |
ret = TRUE; |
DPRINTF(("ldap sudoUser netgroup '%s' ... %s", *p, |
DPRINTF(("ldap sudoUser netgroup '%s' ... %s", val, |
ret ? "MATCH!" : "not"), 2); |
ret ? "MATCH!" : "not"), 2); |
} |
} |
|
|
if (v) |
ldap_value_free_len(bv); /* cleanup */ |
ldap_value_free(v); /* cleanup */ |
|
|
|
return(ret); |
return(ret); |
} |
} |
|
|
LDAP *ld; |
LDAP *ld; |
LDAPMessage *entry; |
LDAPMessage *entry; |
{ |
{ |
char **v = NULL, **p = NULL; |
struct berval **bv, **p; |
|
char *val; |
int ret = FALSE; |
int ret = FALSE; |
|
|
if (!entry) |
if (!entry) |
return(ret); |
return(ret); |
|
|
/* get the values from the entry */ |
/* get the values from the entry */ |
v = ldap_get_values(ld, entry, "sudoHost"); |
bv = ldap_get_values_len(ld, entry, "sudoHost"); |
|
if (bv == NULL) |
|
return(ret); |
|
|
/* walk through values */ |
/* walk through values */ |
for (p = v; p && *p && !ret; p++) { |
for (p = bv; *p != NULL && !ret; p++) { |
|
val = (*p)->bv_val; |
/* match any or address or netgroup or hostname */ |
/* match any or address or netgroup or hostname */ |
if (!strcmp(*p, "ALL") || addr_matches(*p) || |
if (!strcmp(val, "ALL") || addr_matches(val) || |
netgr_matches(*p, user_host, user_shost, NULL) || |
netgr_matches(val, user_host, user_shost, NULL) || |
!hostname_matches(user_shost, user_host, *p)) |
hostname_matches(user_shost, user_host, val)) |
ret = TRUE; |
ret = TRUE; |
DPRINTF(("ldap sudoHost '%s' ... %s", *p, |
DPRINTF(("ldap sudoHost '%s' ... %s", val, |
ret ? "MATCH!" : "not"), 2); |
ret ? "MATCH!" : "not"), 2); |
} |
} |
|
|
if (v) |
ldap_value_free_len(bv); /* cleanup */ |
ldap_value_free(v); /* cleanup */ |
|
|
|
return(ret); |
return(ret); |
} |
} |
|
|
/* |
|
* Walk through search results and return TRUE if we have a runas match, |
|
* else FALSE. |
|
* Since the runas directive in /etc/sudoers is optional, so is sudoRunAs. |
|
*/ |
|
int |
int |
sudo_ldap_check_runas(ld, entry) |
sudo_ldap_check_runas_user(ld, entry) |
LDAP *ld; |
LDAP *ld; |
LDAPMessage *entry; |
LDAPMessage *entry; |
{ |
{ |
char **v = NULL, **p = NULL; |
struct berval **bv, **p; |
|
char *val; |
int ret = FALSE; |
int ret = FALSE; |
|
|
if (!entry) |
if (!runas_pw) |
return(ret); |
return(UNSPEC); |
|
|
/* get the values from the entry */ |
/* get the runas user from the entry */ |
v = ldap_get_values(ld, entry, "sudoRunAs"); |
bv = ldap_get_values_len(ld, entry, "sudoRunAsUser"); |
|
if (bv == NULL) |
|
bv = ldap_get_values_len(ld, entry, "sudoRunAs"); /* old style */ |
|
|
/* |
/* |
* BUG: |
* BUG: |
|
|
* For now just require users to always use -u option unless its set |
* 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 |
* in the global defaults. This behaviour is no different than the global |
* /etc/sudoers. |
* /etc/sudoers. |
* |
* |
* Sigh - maybe add this feature later |
* Sigh - maybe add this feature later |
* |
|
*/ |
*/ |
|
|
/* |
/* |
* If there are no runas entries, match runas_default against |
* If there are no runas entries, match runas_default against |
* what the user specified on the command line. |
* what the user specified on the command line. |
*/ |
*/ |
if (!v) |
if (bv == NULL) |
ret = !strcasecmp(runas_pw->pw_name, def_runas_default); |
return(!strcasecmp(runas_pw->pw_name, def_runas_default)); |
|
|
/* walk through values returned, looking for a match */ |
/* walk through values returned, looking for a match */ |
for (p = v; p && *p && !ret; p++) { |
for (p = bv; *p != NULL && !ret; p++) { |
switch (*p[0]) { |
val = (*p)->bv_val; |
|
switch (val[0]) { |
case '+': |
case '+': |
if (netgr_matches(*p, NULL, NULL, runas_pw->pw_name)) |
if (netgr_matches(val, NULL, NULL, runas_pw->pw_name)) |
ret = TRUE; |
ret = TRUE; |
break; |
break; |
case '%': |
case '%': |
if (usergr_matches(*p, runas_pw->pw_name, runas_pw)) |
if (usergr_matches(val, runas_pw->pw_name, runas_pw)) |
ret = TRUE; |
ret = TRUE; |
break; |
break; |
case 'A': |
case 'A': |
if (strcmp(*p, "ALL") == 0) { |
if (strcmp(val, "ALL") == 0) { |
ret = TRUE; |
ret = TRUE; |
break; |
break; |
} |
} |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
default: |
default: |
if (strcasecmp(*p, runas_pw->pw_name) == 0) |
if (strcasecmp(val, runas_pw->pw_name) == 0) |
ret = TRUE; |
ret = TRUE; |
break; |
break; |
} |
} |
DPRINTF(("ldap sudoRunAs '%s' ... %s", *p, |
DPRINTF(("ldap sudoRunAsUser '%s' ... %s", val, |
ret ? "MATCH!" : "not"), 2); |
ret ? "MATCH!" : "not"), 2); |
} |
} |
|
|
if (v) |
ldap_value_free_len(bv); /* cleanup */ |
ldap_value_free(v); /* cleanup */ |
|
|
|
return(ret); |
return(ret); |
} |
} |
|
|
|
int |
|
sudo_ldap_check_runas_group(ld, entry) |
|
LDAP *ld; |
|
LDAPMessage *entry; |
|
{ |
|
struct berval **bv, **p; |
|
char *val; |
|
int ret = FALSE; |
|
|
|
/* runas_gr is only set if the user specified the -g flag */ |
|
if (!runas_gr) |
|
return(UNSPEC); |
|
|
|
/* get the values from the entry */ |
|
bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup"); |
|
if (bv == NULL) |
|
return(ret); |
|
|
|
/* walk through values returned, looking for a match */ |
|
for (p = bv; *p != NULL && !ret; p++) { |
|
val = (*p)->bv_val; |
|
if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr)) |
|
ret = TRUE; |
|
DPRINTF(("ldap sudoRunAsGroup '%s' ... %s", val, |
|
ret ? "MATCH!" : "not"), 2); |
|
} |
|
|
|
ldap_value_free_len(bv); /* cleanup */ |
|
|
|
return(ret); |
|
} |
|
|
/* |
/* |
* Walk through search results and return TRUE if we have a command match. |
* Walk through search results and return TRUE if we have a runas match, |
|
* else FALSE. RunAs info is optional. |
*/ |
*/ |
int |
int |
|
sudo_ldap_check_runas(ld, entry) |
|
LDAP *ld; |
|
LDAPMessage *entry; |
|
{ |
|
int ret; |
|
|
|
if (!entry) |
|
return(FALSE); |
|
|
|
ret = sudo_ldap_check_runas_user(ld, entry) != FALSE && |
|
sudo_ldap_check_runas_group(ld, entry) != FALSE; |
|
|
|
return(ret); |
|
} |
|
|
|
/* |
|
* Walk through search results and return TRUE if we have a command match, |
|
* FALSE if disallowed and UNSPEC if not matched. |
|
*/ |
|
int |
sudo_ldap_check_command(ld, entry, setenv_implied) |
sudo_ldap_check_command(ld, entry, setenv_implied) |
LDAP *ld; |
LDAP *ld; |
LDAPMessage *entry; |
LDAPMessage *entry; |
int *setenv_implied; |
int *setenv_implied; |
{ |
{ |
char *allowed_cmnd, *allowed_args, **v = NULL, **p = NULL; |
struct berval **bv, **p; |
int foundbang, ret = FALSE; |
char *allowed_cmnd, *allowed_args, *val; |
|
int foundbang, ret = UNSPEC; |
|
|
if (!entry) |
if (!entry) |
return(ret); |
return(ret); |
|
|
v = ldap_get_values(ld, entry, "sudoCommand"); |
bv = ldap_get_values_len(ld, entry, "sudoCommand"); |
|
if (bv == NULL) |
|
return(ret); |
|
|
/* get_first_entry */ |
for (p = bv; *p != NULL && ret != FALSE; p++) { |
for (p = v; p && *p && ret >= 0; p++) { |
val = (*p)->bv_val; |
/* Match against ALL ? */ |
/* Match against ALL ? */ |
if (!strcmp(*p, "ALL")) { |
if (!strcmp(val, "ALL")) { |
ret = TRUE; |
ret = TRUE; |
if (setenv_implied != NULL) |
if (setenv_implied != NULL) |
*setenv_implied = TRUE; |
*setenv_implied = TRUE; |
DPRINTF(("ldap sudoCommand '%s' ... MATCH!", *p), 2); |
DPRINTF(("ldap sudoCommand '%s' ... MATCH!", val), 2); |
continue; |
continue; |
} |
} |
|
|
/* check for !command */ |
/* check for !command */ |
if (**p == '!') { |
if (*val == '!') { |
foundbang = TRUE; |
foundbang = TRUE; |
allowed_cmnd = estrdup(1 + *p); /* !command */ |
allowed_cmnd = estrdup(1 + val); /* !command */ |
} else { |
} else { |
foundbang = FALSE; |
foundbang = FALSE; |
allowed_cmnd = estrdup(*p); /* command */ |
allowed_cmnd = estrdup(val); /* command */ |
} |
} |
|
|
/* split optional args away from command */ |
/* split optional args away from command */ |
|
|
* If allowed (no bang) set ret but keep on checking. |
* If allowed (no bang) set ret but keep on checking. |
* If disallowed (bang), exit loop. |
* If disallowed (bang), exit loop. |
*/ |
*/ |
ret = foundbang ? -1 : TRUE; |
ret = foundbang ? FALSE : TRUE; |
} |
} |
DPRINTF(("ldap sudoCommand '%s' ... %s", *p, |
DPRINTF(("ldap sudoCommand '%s' ... %s", val, |
ret == TRUE ? "MATCH!" : "not"), 2); |
ret == TRUE ? "MATCH!" : "not"), 2); |
|
|
efree(allowed_cmnd); /* cleanup */ |
efree(allowed_cmnd); /* cleanup */ |
} |
} |
|
|
if (v) |
ldap_value_free_len(bv); /* more cleanup */ |
ldap_value_free(v); /* more cleanup */ |
|
|
|
/* return TRUE if we found at least one ALLOW and no DENY */ |
return(ret); |
return(ret > 0); |
|
} |
} |
|
|
/* |
/* |
|
* Search for boolean "option" in sudoOption. |
|
* Returns TRUE if found and allowed, FALSE if negated, else UNSPEC. |
|
*/ |
|
int |
|
sudo_ldap_check_bool(ld, entry, option) |
|
LDAP *ld; |
|
LDAPMessage *entry; |
|
char *option; |
|
{ |
|
struct berval **bv, **p; |
|
char ch, *var; |
|
int ret = UNSPEC; |
|
|
|
if (entry == NULL) |
|
return(UNSPEC); |
|
|
|
bv = ldap_get_values_len(ld, entry, "sudoOption"); |
|
if (bv == NULL) |
|
return(ret); |
|
|
|
/* walk through options */ |
|
for (p = bv; *p != NULL; p++) { |
|
var = (*p)->bv_val;; |
|
DPRINTF(("ldap sudoOption: '%s'", var), 2); |
|
|
|
if ((ch = *var) == '!') |
|
var++; |
|
if (strcmp(var, option) == 0) |
|
ret = (ch != '!'); |
|
} |
|
|
|
ldap_value_free_len(bv); |
|
|
|
return(ret); |
|
} |
|
|
|
/* |
* Read sudoOption and modify the defaults as we go. This is used once |
* Read sudoOption and modify the defaults as we go. This is used once |
* from the cn=defaults entry and also once when a final sudoRole is matched. |
* from the cn=defaults entry and also once when a final sudoRole is matched. |
*/ |
*/ |
|
|
LDAP *ld; |
LDAP *ld; |
LDAPMessage *entry; |
LDAPMessage *entry; |
{ |
{ |
char op, *var, *val, **v = NULL, **p = NULL; |
struct berval **bv, **p; |
|
char op, *var, *val; |
|
|
if (!entry) |
if (entry == NULL) |
return; |
return; |
|
|
v = ldap_get_values(ld, entry, "sudoOption"); |
bv = ldap_get_values_len(ld, entry, "sudoOption"); |
|
if (bv == NULL) |
|
return; |
|
|
/* walk through options */ |
/* walk through options */ |
for (p = v; p && *p; p++) { |
for (p = bv; *p != NULL; p++) { |
|
var = estrdup((*p)->bv_val); |
|
DPRINTF(("ldap sudoOption: '%s'", var), 2); |
|
|
DPRINTF(("ldap sudoOption: '%s'", *p), 2); |
|
var = estrdup(*p); |
|
|
|
/* check for equals sign past first char */ |
/* check for equals sign past first char */ |
val = strchr(var, '='); |
val = strchr(var, '='); |
if (val > var) { |
if (val > var) { |
|
|
efree(var); |
efree(var); |
} |
} |
|
|
if (v) |
ldap_value_free_len(bv); |
ldap_value_free(v); |
|
} |
} |
|
|
/* |
/* |
* Concatenate strings, dynamically growing them as necessary. |
|
* Strings can be arbitrarily long and are allocated/reallocated on |
|
* the fly. Make sure to free them when you are done. |
|
* |
|
* Usage: |
|
* |
|
* char *s=NULL; |
|
* size_t sz; |
|
* |
|
* ncat(&s,&sz,"This "); |
|
* ncat(&s,&sz,"is "); |
|
* ncat(&s,&sz,"an "); |
|
* ncat(&s,&sz,"arbitrarily "); |
|
* ncat(&s,&sz,"long "); |
|
* ncat(&s,&sz,"string!"); |
|
* |
|
* printf("String Value='%s', but has %d bytes allocated\n",s,sz); |
|
* |
|
*/ |
|
void |
|
ncat(s, sz, src) |
|
char **s; |
|
size_t *sz; |
|
char *src; |
|
{ |
|
size_t nsz; |
|
|
|
/* handle initial alloc */ |
|
if (*s == NULL) { |
|
*s = estrdup(src); |
|
*sz = strlen(src) + 1; |
|
return; |
|
} |
|
/* handle realloc */ |
|
nsz = strlen(*s) + strlen(src) + 1; |
|
if (*sz < nsz) |
|
*s = erealloc((void *) *s, *sz = nsz * 2); |
|
strlcat(*s, src, *sz); |
|
} |
|
|
|
/* |
|
* builds together a filter to check against ldap |
* builds together a filter to check against ldap |
*/ |
*/ |
char * |
char * |
sudo_ldap_build_pass1() |
sudo_ldap_build_pass1(pw) |
|
struct passwd *pw; |
{ |
{ |
struct group *grp; |
struct group *grp; |
size_t sz; |
size_t sz; |
char *b = NULL; |
char *buf; |
int i; |
int i; |
|
|
/* global OR */ |
/* Start with (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */ |
ncat(&b, &sz, "(|"); |
sz = 29 + strlen(pw->pw_name); |
|
|
/* build filter sudoUser=user_name */ |
/* Add space for groups */ |
ncat(&b, &sz, "(sudoUser="); |
if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) |
ncat(&b, &sz, user_name); |
sz += 12 + strlen(grp->gr_name); /* primary group */ |
ncat(&b, &sz, ")"); |
for (i = 0; i < user_ngroups; i++) { |
|
if (user_groups[i] == pw->pw_gid) |
|
continue; |
|
if ((grp = sudo_getgrgid(user_groups[i])) != NULL) |
|
sz += 12 + strlen(grp->gr_name); /* supplementary group */ |
|
} |
|
buf = emalloc(sz); |
|
|
|
/* Global OR + sudoUser=user_name filter */ |
|
(void) strlcpy(buf, "(|(sudoUser=", sz); |
|
(void) strlcat(buf, pw->pw_name, sz); |
|
(void) strlcat(buf, ")", sz); |
|
|
/* Append primary group */ |
/* Append primary group */ |
grp = getgrgid(user_gid); |
if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) { |
if (grp != NULL) { |
(void) strlcat(buf, "(sudoUser=%", sz); |
ncat(&b, &sz, "(sudoUser=%"); |
(void) strlcat(buf, grp->gr_name, sz); |
ncat(&b, &sz, grp -> gr_name); |
(void) strlcat(buf, ")", sz); |
ncat(&b, &sz, ")"); |
|
} |
} |
|
|
/* Append supplementary groups */ |
/* Append supplementary groups */ |
for (i = 0; i < user_ngroups; i++) { |
for (i = 0; i < user_ngroups; i++) { |
if (user_groups[i] == user_gid) |
if (user_groups[i] == pw->pw_gid) |
continue; |
continue; |
if ((grp = getgrgid(user_groups[i])) != NULL) { |
if ((grp = sudo_getgrgid(user_groups[i])) != NULL) { |
ncat(&b, &sz, "(sudoUser=%"); |
(void) strlcat(buf, "(sudoUser=%", sz); |
ncat(&b, &sz, grp -> gr_name); |
(void) strlcat(buf, grp->gr_name, sz); |
ncat(&b, &sz, ")"); |
(void) strlcat(buf, ")", sz); |
} |
} |
} |
} |
|
|
/* Add ALL to list */ |
/* Add ALL to list and end the global OR */ |
ncat(&b, &sz, "(sudoUser=ALL)"); |
if (strlcat(buf, "(sudoUser=ALL))", sz) >= sz) |
|
errorx(1, "sudo_ldap_build_pass1 allocation mismatch"); |
|
|
/* End of OR List */ |
return(buf); |
ncat(&b, &sz, ")"); |
|
|
|
return(b); |
|
} |
} |
|
|
/* |
/* |
|
|
return(-1); |
return(-1); |
} |
} |
|
|
|
static void |
|
sudo_ldap_read_secret(path) |
|
const char *path; |
|
{ |
|
FILE *fp; |
|
char buf[LINE_MAX], *cp; |
|
|
|
if ((fp = fopen(_PATH_LDAP_SECRET, "r")) != NULL) { |
|
if (fgets(buf, sizeof(buf), fp) != NULL) { |
|
if ((cp = strchr(buf, '\n')) != NULL) |
|
*cp = '\0'; |
|
/* copy to bindpw and binddn */ |
|
efree(ldap_conf.bindpw); |
|
ldap_conf.bindpw = estrdup(buf); |
|
efree(ldap_conf.binddn); |
|
ldap_conf.binddn = ldap_conf.rootbinddn; |
|
ldap_conf.rootbinddn = NULL; |
|
} |
|
fclose(fp); |
|
} |
|
} |
|
|
int |
int |
sudo_ldap_read_config() |
sudo_ldap_read_config() |
{ |
{ |
FILE *f; |
FILE *fp; |
char buf[LINE_MAX], *c, *keyword, *value; |
char *cp, *keyword, *value; |
struct ldap_config_table *cur; |
struct ldap_config_table *cur; |
|
|
/* defaults */ |
/* defaults */ |
|
|
ldap_conf.tls_checkpeer = -1; |
ldap_conf.tls_checkpeer = -1; |
ldap_conf.timelimit = -1; |
ldap_conf.timelimit = -1; |
ldap_conf.bind_timelimit = -1; |
ldap_conf.bind_timelimit = -1; |
|
ldap_conf.use_sasl = -1; |
|
ldap_conf.rootuse_sasl = -1; |
|
|
if ((f = fopen(_PATH_LDAP_CONF, "r")) == NULL) |
if ((fp = fopen(_PATH_LDAP_CONF, "r")) == NULL) |
return(FALSE); |
return(FALSE); |
|
|
while (fgets(buf, sizeof(buf), f)) { |
while ((cp = sudo_parseln(fp)) != NULL) { |
/* ignore text after comment character */ |
if (*cp == '\0') |
if ((c = strchr(buf, '#')) != NULL) |
|
*c = '\0'; |
|
|
|
/* skip leading whitespace */ |
|
for (c = buf; isspace((unsigned char) *c); c++) |
|
/* nothing */; |
|
|
|
if (*c == '\0' || *c == '\n') |
|
continue; /* skip empty line */ |
continue; /* skip empty line */ |
|
|
/* properly terminate keyword string */ |
/* split into keyword and value */ |
keyword = c; |
keyword = cp; |
while (*c && !isspace((unsigned char) *c)) |
while (*cp && !isblank((unsigned char) *cp)) |
c++; |
cp++; |
if (*c) |
if (*cp) |
*c++ = '\0'; /* terminate keyword */ |
*cp++ = '\0'; /* terminate keyword */ |
|
|
/* skip whitespace before value */ |
/* skip whitespace before value */ |
while (isspace((unsigned char) *c)) |
while (isblank((unsigned char) *cp)) |
c++; |
cp++; |
value = c; |
value = cp; |
|
|
/* trim whitespace after value */ |
|
while (*c) |
|
c++; /* wind to end */ |
|
while (--c > value && isspace((unsigned char) *c)) |
|
*c = '\0'; |
|
|
|
/* Look up keyword in config table. */ |
/* Look up keyword in config table. */ |
for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) { |
for (cur = ldap_conf_table; cur->conf_str != NULL; cur++) { |
if (strcasecmp(keyword, cur->conf_str) == 0) { |
if (strcasecmp(keyword, cur->conf_str) == 0) { |
|
|
} |
} |
} |
} |
} |
} |
fclose(f); |
fclose(fp); |
|
|
if (!ldap_conf.host) |
if (!ldap_conf.host) |
ldap_conf.host = "localhost"; |
ldap_conf.host = estrdup("localhost"); |
|
|
if (ldap_conf.bind_timelimit > 0) |
if (ldap_conf.bind_timelimit > 0) |
ldap_conf.bind_timelimit *= 1000; /* convert to ms */ |
ldap_conf.bind_timelimit *= 1000; /* convert to ms */ |
|
|
fprintf(stderr, "LDAP Config Summary\n"); |
fprintf(stderr, "LDAP Config Summary\n"); |
fprintf(stderr, "===================\n"); |
fprintf(stderr, "===================\n"); |
if (ldap_conf.uri) { |
if (ldap_conf.uri) { |
fprintf(stderr, "uri %s\n", ldap_conf.uri); |
fprintf(stderr, "uri %s\n", ldap_conf.uri); |
} else { |
} else { |
fprintf(stderr, "host %s\n", ldap_conf.host ? |
fprintf(stderr, "host %s\n", ldap_conf.host ? |
ldap_conf.host : "(NONE)"); |
ldap_conf.host : "(NONE)"); |
fprintf(stderr, "port %d\n", ldap_conf.port); |
fprintf(stderr, "port %d\n", ldap_conf.port); |
} |
} |
fprintf(stderr, "ldap_version %d\n", ldap_conf.version); |
fprintf(stderr, "ldap_version %d\n", ldap_conf.version); |
|
|
fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ? |
fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ? |
ldap_conf.base : "(NONE) <---Sudo will ignore ldap)"); |
ldap_conf.base : "(NONE) <---Sudo will ignore ldap)"); |
fprintf(stderr, "binddn %s\n", ldap_conf.binddn ? |
fprintf(stderr, "binddn %s\n", ldap_conf.binddn ? |
ldap_conf.binddn : "(anonymous)"); |
ldap_conf.binddn : "(anonymous)"); |
fprintf(stderr, "bindpw %s\n", ldap_conf.bindpw ? |
fprintf(stderr, "bindpw %s\n", ldap_conf.bindpw ? |
ldap_conf.bindpw : "(anonymous)"); |
ldap_conf.bindpw : "(anonymous)"); |
if (ldap_conf.bind_timelimit > 0) |
if (ldap_conf.bind_timelimit > 0) |
fprintf(stderr, "bind_timelimit %d\n", ldap_conf.bind_timelimit); |
fprintf(stderr, "bind_timelimit %d\n", ldap_conf.bind_timelimit); |
if (ldap_conf.timelimit > 0) |
if (ldap_conf.timelimit > 0) |
fprintf(stderr, "timelimit %d\n", ldap_conf.timelimit); |
fprintf(stderr, "timelimit %d\n", ldap_conf.timelimit); |
fprintf(stderr, "ssl %s\n", ldap_conf.ssl ? |
fprintf(stderr, "ssl %s\n", ldap_conf.ssl ? |
ldap_conf.ssl : "(no)"); |
ldap_conf.ssl : "(no)"); |
if (ldap_conf.tls_checkpeer != -1) |
if (ldap_conf.tls_checkpeer != -1) |
fprintf(stderr, "tls_checkpeer %s\n", ldap_conf.tls_checkpeer ? |
fprintf(stderr, "tls_checkpeer %s\n", ldap_conf.tls_checkpeer ? |
|
|
fprintf(stderr, "tls_certfile %s\n", ldap_conf.tls_certfile); |
fprintf(stderr, "tls_certfile %s\n", ldap_conf.tls_certfile); |
if (ldap_conf.tls_keyfile != NULL) |
if (ldap_conf.tls_keyfile != NULL) |
fprintf(stderr, "tls_keyfile %s\n", ldap_conf.tls_keyfile); |
fprintf(stderr, "tls_keyfile %s\n", ldap_conf.tls_keyfile); |
|
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S |
|
if (ldap_conf.use_sasl != -1) { |
|
fprintf(stderr, "use_sasl %s\n", |
|
ldap_conf.use_sasl ? "yes" : "no"); |
|
fprintf(stderr, "sasl_auth_id %s\n", ldap_conf.sasl_auth_id ? |
|
ldap_conf.sasl_auth_id : "(NONE)"); |
|
fprintf(stderr, "rootuse_sasl %d\n", ldap_conf.rootuse_sasl); |
|
fprintf(stderr, "rootsasl_auth_id %s\n", ldap_conf.rootsasl_auth_id ? |
|
ldap_conf.rootsasl_auth_id : "(NONE)"); |
|
fprintf(stderr, "sasl_secprops %s\n", ldap_conf.sasl_secprops ? |
|
ldap_conf.sasl_secprops : "(NONE)"); |
|
fprintf(stderr, "krb5_ccname %s\n", ldap_conf.krb5_ccname ? |
|
ldap_conf.krb5_ccname : "(NONE)"); |
|
} |
|
#endif |
fprintf(stderr, "===================\n"); |
fprintf(stderr, "===================\n"); |
} |
} |
if (!ldap_conf.base) |
if (!ldap_conf.base) |
|
|
} |
} |
#endif |
#endif |
|
|
/* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */ |
if (!ldap_conf.uri) { |
if (!ldap_conf.uri && ldap_conf.port < 0) |
/* Use port 389 for plaintext LDAP and port 636 for SSL LDAP */ |
ldap_conf.port = |
if (ldap_conf.port < 0) |
ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT; |
ldap_conf.port = |
|
ldap_conf.ssl_mode == SUDO_LDAP_SSL ? LDAPS_PORT : LDAP_PORT; |
|
|
|
#ifdef HAVE_LDAP_CREATE |
|
/* |
|
* Cannot specify port directly to ldap_create(), each host must |
|
* include :port to override the default. |
|
*/ |
|
if (ldap_conf.port != LDAP_PORT) |
|
sudo_ldap_conf_add_ports(); |
|
#endif |
|
} |
|
|
/* If rootbinddn set, read in /etc/ldap.secret if it exists. */ |
/* If rootbinddn set, read in /etc/ldap.secret if it exists. */ |
if (ldap_conf.rootbinddn) { |
if (ldap_conf.rootbinddn) |
if ((f = fopen(_PATH_LDAP_SECRET, "r")) != NULL) { |
sudo_ldap_read_secret(_PATH_LDAP_SECRET); |
if (fgets(buf, sizeof(buf), f) != NULL) { |
|
/* removing trailing newlines */ |
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S |
for (c = buf; *c != '\0'; c++) |
/* |
continue; |
* Make sure we can open the file specified by krb5_ccname. |
while (--c > buf && *c == '\n') |
*/ |
*c = '\0'; |
if (ldap_conf.krb5_ccname != NULL) { |
/* copy to bindpw and binddn */ |
if (strncasecmp(ldap_conf.krb5_ccname, "FILE:", 5) == 0 || |
efree(ldap_conf.bindpw); |
strncasecmp(ldap_conf.krb5_ccname, "WRFILE:", 7) == 0) { |
ldap_conf.bindpw = estrdup(buf); |
value = ldap_conf.krb5_ccname + |
efree(ldap_conf.binddn); |
(ldap_conf.krb5_ccname[4] == ':' ? 5 : 7); |
ldap_conf.binddn = ldap_conf.rootbinddn; |
if ((fp = fopen(value, "r")) != NULL) { |
ldap_conf.rootbinddn = NULL; |
DPRINTF(("using krb5 credential cache: %s", value), 1); |
|
fclose(fp); |
|
} else { |
|
/* Can't open it, just ignore the entry. */ |
|
DPRINTF(("unable to open krb5 credential cache: %s", value), 1); |
|
efree(ldap_conf.krb5_ccname); |
|
ldap_conf.krb5_ccname = NULL; |
} |
} |
fclose(f); |
|
} |
} |
} |
} |
|
#endif |
return(TRUE); |
return(TRUE); |
} |
} |
|
|
/* |
/* |
* like perl's join(sep,@ARGS) |
* Extract the dn from an entry and return the first rdn from it. |
*/ |
*/ |
char * |
static char * |
_ldap_join_values(sep, v) |
sudo_ldap_get_first_rdn(ld, entry) |
char *sep; |
LDAP *ld; |
char **v; |
LDAPMessage *entry; |
{ |
{ |
char *b = NULL, **p = NULL; |
#ifdef HAVE_LDAP_STR2DN |
size_t sz = 0; |
char *dn, *rdn = NULL; |
|
LDAPDN tmpDN; |
|
|
/* paste values together */ |
if ((dn = ldap_get_dn(ld, entry)) == NULL) |
for (p = v; p && *p; p++) { |
return(NULL); |
if (p != v && sep != NULL) |
if (ldap_str2dn(dn, &tmpDN, LDAP_DN_FORMAT_LDAP) == LDAP_SUCCESS) { |
ncat(&b, &sz, sep); /* append separator */ |
ldap_rdn2str(tmpDN[0], &rdn, LDAP_DN_FORMAT_UFN); |
ncat(&b, &sz, *p); /* append value */ |
ldap_dnfree(tmpDN); |
} |
} |
|
ldap_memfree(dn); |
|
return(rdn); |
|
#else |
|
char *dn, **edn; |
|
|
/* sanity check */ |
if ((dn = ldap_get_dn(ld, entry)) == NULL) |
if (b[0] == '\0') { |
return(NULL); |
/* something went wrong, put something here */ |
edn = ldap_explode_dn(dn, 1); |
ncat(&b, &sz, "(empty list)"); /* append value */ |
ldap_memfree(dn); |
|
return(edn ? edn[0] : NULL); |
|
#endif |
|
} |
|
|
|
/* |
|
* Fetch and display the global Options. |
|
*/ |
|
int |
|
sudo_ldap_display_defaults(nss, pw, lbuf) |
|
struct sudo_nss *nss; |
|
struct passwd *pw; |
|
struct lbuf *lbuf; |
|
{ |
|
struct berval **bv, **p; |
|
LDAP *ld = (LDAP *) nss->handle; |
|
LDAPMessage *entry = NULL, *result = NULL; |
|
char *prefix = NULL; |
|
int rc, count = 0; |
|
|
|
if (ld == NULL) |
|
return(-1); |
|
|
|
rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, |
|
"cn=defaults", NULL, 0, NULL, NULL, NULL, -1, &result); |
|
if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { |
|
bv = ldap_get_values_len(ld, entry, "sudoOption"); |
|
if (bv != NULL) { |
|
if (lbuf->len == 0) |
|
prefix = " "; |
|
else |
|
prefix = ", "; |
|
for (p = bv; *p != NULL; p++) { |
|
lbuf_append(lbuf, prefix, (*p)->bv_val, NULL); |
|
prefix = ", "; |
|
count++; |
|
} |
|
ldap_value_free_len(bv); |
|
} |
} |
} |
|
if (result) |
|
ldap_msgfree(result); |
|
return(count); |
|
} |
|
|
return(b); |
/* |
|
* STUB |
|
*/ |
|
int |
|
sudo_ldap_display_bound_defaults(nss, pw, lbuf) |
|
struct sudo_nss *nss; |
|
struct passwd *pw; |
|
struct lbuf *lbuf; |
|
{ |
|
return(1); |
} |
} |
|
|
char *sudo_ldap_cm_list = NULL; |
/* |
size_t sudo_ldap_cm_list_size; |
* Print a record in the short form, ala file sudoers. |
|
*/ |
|
int |
|
sudo_ldap_display_entry_short(ld, entry, lbuf) |
|
LDAP *ld; |
|
LDAPMessage *entry; |
|
struct lbuf *lbuf; |
|
{ |
|
struct berval **bv, **p; |
|
int count = 0; |
|
|
#define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x)) |
lbuf_append(lbuf, " (", NULL); |
|
|
|
/* get the RunAsUser Values from the entry */ |
|
bv = ldap_get_values_len(ld, entry, "sudoRunAsUser"); |
|
if (bv == NULL) |
|
bv = ldap_get_values_len(ld, entry, "sudoRunAs"); |
|
if (bv != NULL) { |
|
for (p = bv; *p != NULL; p++) { |
|
if (p != bv) |
|
lbuf_append(lbuf, ", ", NULL); |
|
lbuf_append(lbuf, (*p)->bv_val, NULL); |
|
} |
|
ldap_value_free_len(bv); |
|
} else |
|
lbuf_append(lbuf, def_runas_default, NULL); |
|
|
|
/* get the RunAsGroup Values from the entry */ |
|
bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup"); |
|
if (bv != NULL) { |
|
lbuf_append(lbuf, " : ", NULL); |
|
for (p = bv; *p != NULL; p++) { |
|
if (p != bv) |
|
lbuf_append(lbuf, ", ", NULL); |
|
lbuf_append(lbuf, (*p)->bv_val, NULL); |
|
} |
|
ldap_value_free_len(bv); |
|
} |
|
lbuf_append(lbuf, ") ", NULL); |
|
|
|
/* get the Option Values from the entry */ |
|
bv = ldap_get_values_len(ld, entry, "sudoOption"); |
|
if (bv != NULL) { |
|
char *cp, *tag; |
|
|
|
for (p = bv; *p != NULL; p++) { |
|
cp = (*p)->bv_val; |
|
if (*cp == '!') |
|
cp++; |
|
tag = NULL; |
|
if (strcmp(cp, "authenticate") == 0) |
|
tag = (*p)->bv_val[0] == '!' ? |
|
"NOPASSWD: " : "PASSWD: "; |
|
else if (strcmp(cp, "noexec") == 0) |
|
tag = (*p)->bv_val[0] == '!' ? |
|
"EXEC: " : "NOEXEC: "; |
|
else if (strcmp(cp, "setenv") == 0) |
|
tag = (*p)->bv_val[0] == '!' ? |
|
"NOSETENV: " : "SETENV: "; |
|
if (tag != NULL) |
|
lbuf_append(lbuf, tag, NULL); |
|
/* XXX - ignores other options */ |
|
} |
|
ldap_value_free_len(bv); |
|
} |
|
|
|
/* get the Command Values from the entry */ |
|
bv = ldap_get_values_len(ld, entry, "sudoCommand"); |
|
if (bv != NULL) { |
|
for (p = bv; *p != NULL; p++) { |
|
if (p != bv) |
|
lbuf_append(lbuf, ", ", NULL); |
|
lbuf_append(lbuf, (*p)->bv_val, NULL); |
|
count++; |
|
} |
|
ldap_value_free_len(bv); |
|
} |
|
|
|
lbuf_print(lbuf); /* forces a newline */ |
|
return(count); |
|
} |
|
|
/* |
/* |
* Walks through search result and returns TRUE if we have a |
* Print a record in the long form. |
* command match |
|
*/ |
*/ |
int |
int |
sudo_ldap_add_match(ld, entry, pwflag) |
sudo_ldap_display_entry_long(ld, entry, lbuf) |
LDAP *ld; |
LDAP *ld; |
LDAPMessage *entry; |
LDAPMessage *entry; |
int pwflag; |
struct lbuf *lbuf; |
{ |
{ |
char *dn, **edn, **v = NULL; |
struct berval **bv, **p; |
|
char *rdn; |
|
int count = 0; |
|
|
/* if we are not collecting matches, then don't save them */ |
/* extract the dn, only show the first rdn */ |
if (pwflag != I_LISTPW) |
rdn = sudo_ldap_get_first_rdn(ld, entry); |
return(TRUE); |
lbuf_print(lbuf); /* force a newline */ |
|
lbuf_append(lbuf, "LDAP Role: ", rdn ? rdn : "UNKNOWN", NULL); |
|
lbuf_print(lbuf); |
|
if (rdn) |
|
ldap_memfree(rdn); |
|
|
/* collect the dn, only show the rdn */ |
/* get the RunAsUser Values from the entry */ |
dn = ldap_get_dn(ld, entry); |
lbuf_append(lbuf, " RunAsUsers: ", NULL); |
edn = dn ? ldap_explode_dn(dn, 1) : NULL; |
bv = ldap_get_values_len(ld, entry, "sudoRunAsUser"); |
SAVE_LIST("\nLDAP Role: "); |
if (bv == NULL) |
SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN"); |
bv = ldap_get_values_len(ld, entry, "sudoRunAs"); |
SAVE_LIST("\n"); |
if (bv != NULL) { |
if (dn) |
for (p = bv; *p != NULL; p++) { |
ldap_memfree(dn); |
if (p != bv) |
if (edn) |
lbuf_append(lbuf, ", ", NULL); |
ldap_value_free(edn); |
lbuf_append(lbuf, (*p)->bv_val, NULL); |
|
} |
|
ldap_value_free_len(bv); |
|
} else |
|
lbuf_append(lbuf, def_runas_default, NULL); |
|
lbuf_print(lbuf); |
|
|
/* get the Runas Values from the entry */ |
/* get the RunAsGroup Values from the entry */ |
v = ldap_get_values(ld, entry, "sudoRunAs"); |
bv = ldap_get_values_len(ld, entry, "sudoRunAsGroup"); |
if (v && *v) { |
if (bv != NULL) { |
SAVE_LIST(" RunAs: ("); |
lbuf_append(lbuf, " RunAsGroups: ", NULL); |
SAVE_LIST(_ldap_join_values(", ", v)); |
for (p = bv; *p != NULL; p++) { |
SAVE_LIST(")\n"); |
if (p != bv) |
|
lbuf_append(lbuf, ", ", NULL); |
|
lbuf_append(lbuf, (*p)->bv_val, NULL); |
|
} |
|
ldap_value_free_len(bv); |
|
lbuf_print(lbuf); |
} |
} |
if (v) |
|
ldap_value_free(v); |
|
|
|
|
/* get the Option Values from the entry */ |
|
bv = ldap_get_values_len(ld, entry, "sudoOption"); |
|
if (bv != NULL) { |
|
lbuf_append(lbuf, " Options: ", NULL); |
|
for (p = bv; *p != NULL; p++) { |
|
if (p != bv) |
|
lbuf_append(lbuf, ", ", NULL); |
|
lbuf_append(lbuf, (*p)->bv_val, NULL); |
|
} |
|
ldap_value_free_len(bv); |
|
lbuf_print(lbuf); |
|
} |
|
|
/* get the Command Values from the entry */ |
/* get the Command Values from the entry */ |
v = ldap_get_values(ld, entry, "sudoCommand"); |
bv = ldap_get_values_len(ld, entry, "sudoCommand"); |
if (v && *v) { |
if (bv != NULL) { |
SAVE_LIST(" Commands:\n "); |
lbuf_append(lbuf, " Commands:", NULL); |
SAVE_LIST(_ldap_join_values("\n ", v)); |
lbuf_print(lbuf); |
SAVE_LIST("\n"); |
for (p = bv; *p != NULL; p++) { |
} else { |
lbuf_append(lbuf, "\t", (*p)->bv_val, NULL); |
SAVE_LIST(" Commands: NONE\n"); |
lbuf_print(lbuf); |
|
count++; |
|
} |
|
ldap_value_free_len(bv); |
} |
} |
if (v) |
|
ldap_value_free(v); |
|
|
|
return(FALSE); /* Don't stop at the first match */ |
return(count); |
} |
} |
#undef SAVE_LIST |
|
|
|
void |
/* |
sudo_ldap_list_matches() |
* Like sudo_ldap_lookup(), except we just print entries. |
|
*/ |
|
int |
|
sudo_ldap_display_privs(nss, pw, lbuf) |
|
struct sudo_nss *nss; |
|
struct passwd *pw; |
|
struct lbuf *lbuf; |
{ |
{ |
if (sudo_ldap_cm_list != NULL) |
LDAP *ld = (LDAP *) nss->handle; |
printf("%s", sudo_ldap_cm_list); |
LDAPMessage *entry = NULL, *result = NULL; |
|
char *filt; |
|
int rc, do_netgr, count = 0; |
|
|
|
if (ld == NULL) |
|
return(-1); |
|
|
|
/* |
|
* Okay - time to search for anything that matches this user |
|
* Lets limit it to only two queries of the LDAP server |
|
* |
|
* The first pass will look by the username, groups, and |
|
* the keyword ALL. We will then inspect the results that |
|
* came back from the query. We don't need to inspect the |
|
* sudoUser in this pass since the LDAP server already scanned |
|
* it for us. |
|
* |
|
* The second pass will return all the entries that contain |
|
* user netgroups. Then we take the netgroups returned and |
|
* try to match them against the username. |
|
*/ |
|
for (do_netgr = 0; do_netgr < 2; do_netgr++) { |
|
filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); |
|
DPRINTF(("ldap search '%s'", filt), 1); |
|
rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, |
|
NULL, 0, NULL, NULL, NULL, -1, &result); |
|
efree(filt); |
|
if (rc != LDAP_SUCCESS) |
|
continue; /* no entries for this pass */ |
|
|
|
/* print each matching entry */ |
|
LDAP_FOREACH(entry, ld, result) { |
|
if ((!do_netgr || |
|
sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) && |
|
sudo_ldap_check_host(ld, entry)) { |
|
|
|
if (long_list) |
|
count += sudo_ldap_display_entry_long(ld, entry, lbuf); |
|
else |
|
count += sudo_ldap_display_entry_short(ld, entry, lbuf); |
|
} |
|
} |
|
ldap_msgfree(result); |
|
result = NULL; |
|
} |
|
return(count); |
} |
} |
|
|
|
int |
|
sudo_ldap_display_cmnd(nss, pw) |
|
struct sudo_nss *nss; |
|
struct passwd *pw; |
|
{ |
|
LDAP *ld = (LDAP *) nss->handle; |
|
LDAPMessage *entry = NULL, *result = NULL; /* used for searches */ |
|
char *filt; /* used to parse attributes */ |
|
int rc, found, do_netgr; /* temp/final return values */ |
|
|
|
if (ld == NULL) |
|
return(1); |
|
|
|
/* |
|
* Okay - time to search for anything that matches this user |
|
* Lets limit it to only two queries of the LDAP server |
|
* |
|
* The first pass will look by the username, groups, and |
|
* the keyword ALL. We will then inspect the results that |
|
* came back from the query. We don't need to inspect the |
|
* sudoUser in this pass since the LDAP server already scanned |
|
* it for us. |
|
* |
|
* The second pass will return all the entries that contain |
|
* user netgroups. Then we take the netgroups returned and |
|
* try to match them against the username. |
|
*/ |
|
for (found = FALSE, do_netgr = 0; !found && do_netgr < 2; do_netgr++) { |
|
filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); |
|
DPRINTF(("ldap search '%s'", filt), 1); |
|
rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, |
|
NULL, 0, NULL, NULL, NULL, -1, &result); |
|
efree(filt); |
|
if (rc != LDAP_SUCCESS) |
|
continue; /* no entries for this pass */ |
|
|
|
LDAP_FOREACH(entry, ld, result) { |
|
if ((!do_netgr || |
|
sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) && |
|
sudo_ldap_check_host(ld, entry) && |
|
sudo_ldap_check_command(ld, entry, NULL) && |
|
sudo_ldap_check_runas(ld, entry)) { |
|
|
|
found = TRUE; |
|
break; |
|
} |
|
} |
|
ldap_msgfree(result); |
|
result = NULL; |
|
} |
|
|
|
if (found) |
|
printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd, |
|
user_args ? " " : "", user_args ? user_args : ""); |
|
return(!found); |
|
} |
|
|
|
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S |
|
static int |
|
sudo_ldap_sasl_interact(ld, flags, _auth_id, _interact) |
|
LDAP *ld; |
|
unsigned int flags; |
|
void *_auth_id; |
|
void *_interact; |
|
{ |
|
char *auth_id = (char *)_auth_id; |
|
sasl_interact_t *interact = (sasl_interact_t *)_interact; |
|
|
|
for (; interact->id != SASL_CB_LIST_END; interact++) { |
|
if (interact->id != SASL_CB_USER) |
|
return(LDAP_PARAM_ERROR); |
|
|
|
if (auth_id != NULL) |
|
interact->result = auth_id; |
|
else if (interact->defresult != NULL) |
|
interact->result = interact->defresult; |
|
else |
|
interact->result = ""; |
|
|
|
interact->len = strlen(interact->result); |
|
#if SASL_VERSION_MAJOR < 2 |
|
interact->result = estrdup(interact->result); |
|
#endif /* SASL_VERSION_MAJOR < 2 */ |
|
} |
|
return(LDAP_SUCCESS); |
|
} |
|
#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */ |
|
|
/* |
/* |
* Set LDAP options based on the config table. |
* Set LDAP options based on the config table. |
*/ |
*/ |
|
|
if (ival >= 0) { |
if (ival >= 0) { |
rc = ldap_set_option(conn, cur->opt_val, &ival); |
rc = ldap_set_option(conn, cur->opt_val, &ival); |
if (rc != LDAP_OPT_SUCCESS) { |
if (rc != LDAP_OPT_SUCCESS) { |
warnx("ldap_set_option: %s -> %d: %s", |
warningx("ldap_set_option: %s -> %d: %s", |
cur->conf_str, ival, ldap_err2string(rc)); |
cur->conf_str, ival, ldap_err2string(rc)); |
return(-1); |
return(-1); |
} |
} |
|
|
if (sval != NULL) { |
if (sval != NULL) { |
rc = ldap_set_option(conn, cur->opt_val, sval); |
rc = ldap_set_option(conn, cur->opt_val, sval); |
if (rc != LDAP_OPT_SUCCESS) { |
if (rc != LDAP_OPT_SUCCESS) { |
warnx("ldap_set_option: %s -> %s: %s", |
warningx("ldap_set_option: %s -> %s: %s", |
cur->conf_str, sval, ldap_err2string(rc)); |
cur->conf_str, sval, ldap_err2string(rc)); |
return(-1); |
return(-1); |
} |
} |
|
|
tv.tv_usec = 0; |
tv.tv_usec = 0; |
rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv); |
rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv); |
if (rc != LDAP_OPT_SUCCESS) { |
if (rc != LDAP_OPT_SUCCESS) { |
warnx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s", |
warningx("ldap_set_option(NETWORK_TIMEOUT, %ld): %s", |
(long)tv.tv_sec, ldap_err2string(rc)); |
(long)tv.tv_sec, ldap_err2string(rc)); |
return(-1); |
return(-1); |
} |
} |
|
|
int val = LDAP_OPT_X_TLS_HARD; |
int val = LDAP_OPT_X_TLS_HARD; |
rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val); |
rc = ldap_set_option(ld, LDAP_OPT_X_TLS, &val); |
if (rc != LDAP_SUCCESS) { |
if (rc != LDAP_SUCCESS) { |
warnx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s", |
warningx("ldap_set_option(LDAP_OPT_X_TLS, LDAP_OPT_X_TLS_HARD): %s", |
ldap_err2string(rc)); |
ldap_err2string(rc)); |
return(-1); |
return(-1); |
} |
} |
|
|
} |
} |
|
|
/* |
/* |
* Open a connection to the LDAP server. |
* Connect to the LDAP server specified by ld |
*/ |
*/ |
static LDAP * |
static int |
sudo_ldap_open() |
sudo_ldap_bind_s(ld) |
|
LDAP *ld; |
{ |
{ |
LDAP *ld = NULL; |
|
int rc; |
int rc; |
|
const char *old_ccname = user_ccname; |
|
#ifdef HAVE_GSS_KRB5_CCACHE_NAME |
|
unsigned int status; |
|
#endif |
|
|
|
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S |
|
if (ldap_conf.rootuse_sasl == TRUE || |
|
(ldap_conf.rootuse_sasl != FALSE && ldap_conf.use_sasl == TRUE)) { |
|
void *auth_id = ldap_conf.rootsasl_auth_id ? |
|
ldap_conf.rootsasl_auth_id : ldap_conf.sasl_auth_id; |
|
|
|
if (ldap_conf.krb5_ccname != NULL) { |
|
#ifdef HAVE_GSS_KRB5_CCACHE_NAME |
|
if (gss_krb5_ccache_name(&status, ldap_conf.krb5_ccname, &old_ccname) |
|
!= GSS_S_COMPLETE) { |
|
old_ccname = NULL; |
|
DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1); |
|
} |
|
#else |
|
sudo_setenv("KRB5CCNAME", ldap_conf.krb5_ccname, TRUE); |
|
#endif |
|
} |
|
rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI", |
|
NULL, NULL, LDAP_SASL_QUIET, sudo_ldap_sasl_interact, auth_id); |
|
if (ldap_conf.krb5_ccname != NULL) { |
|
#ifdef HAVE_GSS_KRB5_CCACHE_NAME |
|
if (gss_krb5_ccache_name(&status, old_ccname, NULL) != GSS_S_COMPLETE) |
|
DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1); |
|
#else |
|
if (old_ccname != NULL) |
|
sudo_setenv("KRB5CCNAME", old_ccname, TRUE); |
|
else |
|
sudo_unsetenv("KRB5CCNAME"); |
|
#endif |
|
} |
|
if (rc != LDAP_SUCCESS) { |
|
warningx("ldap_sasl_interactive_bind_s(): %s", ldap_err2string(rc)); |
|
return(-1); |
|
} |
|
DPRINTF(("ldap_sasl_interactive_bind_s() ok"), 1); |
|
} else |
|
#endif /* HAVE_LDAP_SASL_INTERACTIVE_BIND_S */ |
|
#ifdef HAVE_LDAP_SASL_BIND_S |
|
{ |
|
struct berval bv; |
|
|
|
bv.bv_val = ldap_conf.bindpw ? ldap_conf.bindpw : ""; |
|
bv.bv_len = strlen(bv.bv_val); |
|
|
|
rc = ldap_sasl_bind_s(ld, ldap_conf.binddn, LDAP_SASL_SIMPLE, &bv, |
|
NULL, NULL, NULL); |
|
if (rc != LDAP_SUCCESS) { |
|
warningx("ldap_sasl_bind_s(): %s", ldap_err2string(rc)); |
|
return(-1); |
|
} |
|
DPRINTF(("ldap_sasl_bind_s() ok"), 1); |
|
} |
|
#else |
|
{ |
|
rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw); |
|
if (rc != LDAP_SUCCESS) { |
|
warningx("ldap_simple_bind_s(): %s", ldap_err2string(rc)); |
|
return(-1); |
|
} |
|
DPRINTF(("ldap_simple_bind_s() ok"), 1); |
|
} |
|
#endif |
|
return(0); |
|
} |
|
|
|
/* |
|
* Open a connection to the LDAP server. |
|
* Returns 0 on success and non-zero on failure. |
|
*/ |
|
int |
|
sudo_ldap_open(nss) |
|
struct sudo_nss *nss; |
|
{ |
|
LDAP *ld; |
|
int rc, ldapnoinit = FALSE; |
|
|
if (!sudo_ldap_read_config()) |
if (!sudo_ldap_read_config()) |
return(NULL); |
return(-1); |
|
|
|
/* Prevent reading of user ldaprc and system defaults. */ |
|
if (getenv("LDAPNOINIT") == NULL) { |
|
ldapnoinit = TRUE; |
|
sudo_setenv("LDAPNOINIT", "1", TRUE); |
|
} |
|
|
/* Connect to LDAP server */ |
/* Connect to LDAP server */ |
#ifdef HAVE_LDAP_INITIALIZE |
#ifdef HAVE_LDAP_INITIALIZE |
if (ldap_conf.uri != NULL) { |
if (ldap_conf.uri != NULL) { |
DPRINTF(("ldap_initialize(ld, %s)", ldap_conf.uri), 2); |
DPRINTF(("ldap_initialize(ld, %s)", ldap_conf.uri), 2); |
rc = ldap_initialize(&ld, ldap_conf.uri); |
rc = ldap_initialize(&ld, ldap_conf.uri); |
} else |
} else |
#endif /* HAVE_LDAP_INITIALIZE */ |
#endif |
rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port); |
rc = sudo_ldap_init(&ld, ldap_conf.host, ldap_conf.port); |
if (rc != LDAP_SUCCESS) { |
if (rc != LDAP_SUCCESS) { |
warnx("unable to initialize LDAP: %s", ldap_err2string(rc)); |
warningx("unable to initialize LDAP: %s", ldap_err2string(rc)); |
return(NULL); |
return(-1); |
} |
} |
|
|
|
if (ldapnoinit) |
|
sudo_unsetenv("LDAPNOINIT"); |
|
|
/* Set LDAP options */ |
/* Set LDAP options */ |
if (sudo_ldap_set_options(ld) < 0) |
if (sudo_ldap_set_options(ld) < 0) |
return(NULL); |
return(-1); |
|
|
if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) { |
if (ldap_conf.ssl_mode == SUDO_LDAP_STARTTLS) { |
#ifdef HAVE_LDAP_START_TLS_S |
#ifdef HAVE_LDAP_START_TLS_S |
rc = ldap_start_tls_s(ld, NULL, NULL); |
rc = ldap_start_tls_s(ld, NULL, NULL); |
if (rc != LDAP_SUCCESS) { |
if (rc != LDAP_SUCCESS) { |
warnx("ldap_start_tls_s(): %s", ldap_err2string(rc)); |
warningx("ldap_start_tls_s(): %s", ldap_err2string(rc)); |
ldap_unbind(ld); |
return(-1); |
return(NULL); |
|
} |
} |
DPRINTF(("ldap_start_tls_s() ok"), 1); |
DPRINTF(("ldap_start_tls_s() ok"), 1); |
#else |
#else |
warnx("start_tls specified but LDAP libs do not support ldap_start_tls_s()"); |
warningx("start_tls specified but LDAP libs do not support ldap_start_tls_s()"); |
#endif /* HAVE_LDAP_START_TLS_S */ |
#endif /* HAVE_LDAP_START_TLS_S */ |
} |
} |
|
|
/* Actually connect */ |
/* Actually connect */ |
if ((rc = ldap_simple_bind_s(ld, ldap_conf.binddn, ldap_conf.bindpw))) { |
if (sudo_ldap_bind_s(ld) != 0) |
warnx("ldap_simple_bind_s: %s", ldap_err2string(rc)); |
return(-1); |
return(NULL); |
|
} |
|
DPRINTF(("ldap_simple_bind_s() ok"), 1); |
|
|
|
return(ld); |
nss->handle = ld; |
|
return(0); |
} |
} |
|
|
static void |
int |
sudo_ldap_update_defaults(ld) |
sudo_ldap_setdefs(nss) |
LDAP *ld; |
struct sudo_nss *nss; |
{ |
{ |
|
LDAP *ld = (LDAP *) nss->handle; |
LDAPMessage *entry = NULL, *result = NULL; /* used for searches */ |
LDAPMessage *entry = NULL, *result = NULL; /* used for searches */ |
int rc; /* temp return value */ |
int rc; /* temp return value */ |
|
|
rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, |
if (ld == NULL) |
"cn=defaults", NULL, 0, &result); |
return(-1); |
if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { |
|
|
rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, |
|
"cn=defaults", NULL, 0, NULL, NULL, NULL, -1, &result); |
|
if (rc == 0 && (entry = ldap_first_entry(ld, result))) { |
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1); |
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1); |
sudo_ldap_parse_options(ld, entry); |
sudo_ldap_parse_options(ld, entry); |
} else |
} else |
|
|
|
|
if (result) |
if (result) |
ldap_msgfree(result); |
ldap_msgfree(result); |
|
|
|
return(0); |
} |
} |
|
|
/* |
/* |
* like sudoers_lookup() - only LDAP style |
* like sudoers_lookup() - only LDAP style |
*/ |
*/ |
int |
int |
sudo_ldap_check(pwflag) |
sudo_ldap_lookup(nss, ret, pwflag) |
|
struct sudo_nss *nss; |
|
int ret; |
int pwflag; |
int pwflag; |
{ |
{ |
LDAP *ld; |
LDAP *ld = (LDAP *) nss->handle; |
LDAPMessage *entry = NULL, *result = NULL; /* used for searches */ |
LDAPMessage *entry = NULL, *result = NULL; |
char *filt; /* used to parse attributes */ |
char *filt; |
int rc, ret = FALSE, do_netgr; /* temp/final return values */ |
int do_netgr, rc, matched; |
int setenv_implied; |
int setenv_implied; |
int ldap_user_matches = FALSE, ldap_host_matches = FALSE; /* flags */ |
int ldap_user_matches = FALSE, ldap_host_matches = FALSE; |
|
struct passwd *pw = list_pw ? list_pw : sudo_user.pw; |
|
|
/* Open a connection to the LDAP server. */ |
if (ld == NULL) |
if ((ld = sudo_ldap_open()) == NULL) |
return(ret); |
return(VALIDATE_ERROR); |
|
|
|
/* Parse Default options. */ |
if (pwflag) { |
sudo_ldap_update_defaults(ld); |
int doauth = UNSPEC; |
|
enum def_tupple pwcheck = |
|
(pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; |
|
|
|
for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) { |
|
filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); |
|
rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, |
|
NULL, 0, NULL, NULL, NULL, -1, &result); |
|
efree(filt); |
|
if (rc != LDAP_SUCCESS) |
|
continue; |
|
|
|
LDAP_FOREACH(entry, ld, result) { |
|
/* only verify netgroup matches in pass 2 */ |
|
if (do_netgr && !sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) |
|
continue; |
|
|
|
ldap_user_matches = TRUE; |
|
if (sudo_ldap_check_host(ld, entry)) { |
|
ldap_host_matches = TRUE; |
|
if ((pwcheck == any && doauth != FALSE) || |
|
(pwcheck == all && doauth == FALSE)) |
|
doauth = sudo_ldap_check_bool(ld, entry, "authenticate"); |
|
/* Only check the command when listing another user. */ |
|
if (user_uid == 0 || list_pw == NULL || |
|
user_uid == list_pw->pw_uid || |
|
sudo_ldap_check_command(ld, entry, NULL)) { |
|
matched = 1; |
|
break; /* end foreach */ |
|
} |
|
} |
|
} |
|
ldap_msgfree(result); |
|
result = NULL; |
|
} |
|
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; |
|
} |
|
|
/* |
/* |
* Okay - time to search for anything that matches this user |
* Okay - time to search for anything that matches this user |
* Lets limit it to only two queries of the LDAP server |
* Lets limit it to only two queries of the LDAP server |
|
|
* try to match them against the username. |
* try to match them against the username. |
*/ |
*/ |
setenv_implied = FALSE; |
setenv_implied = FALSE; |
for (do_netgr = 0; !ret && do_netgr < 2; do_netgr++) { |
for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) { |
filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(); |
filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); |
DPRINTF(("ldap search '%s'", filt), 1); |
DPRINTF(("ldap search '%s'", filt), 1); |
rc = ldap_search_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, |
rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, |
NULL, 0, &result); |
NULL, 0, NULL, NULL, NULL, -1, &result); |
if (rc != LDAP_SUCCESS) |
if (rc != LDAP_SUCCESS) |
DPRINTF(("nothing found for '%s'", filt), 1); |
DPRINTF(("nothing found for '%s'", filt), 1); |
efree(filt); |
efree(filt); |
|
|
/* parse each entry returned from this most recent search */ |
/* parse each entry returned from this most recent search */ |
entry = rc ? NULL : ldap_first_entry(ld, result); |
if (rc == LDAP_SUCCESS) { |
while (entry != NULL) { |
LDAP_FOREACH(entry, ld, result) { |
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1); |
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1); |
if ( |
if ( |
/* first verify user netgroup matches - only if in pass 2 */ |
/* first verify user netgroup matches - only if in pass 2 */ |
(!do_netgr || sudo_ldap_check_user_netgroup(ld, entry)) && |
(!do_netgr || sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) && |
/* remember that user matched */ |
/* remember that user matched */ |
(ldap_user_matches = -1) && |
(ldap_user_matches = TRUE) && |
/* verify host match */ |
/* verify host match */ |
sudo_ldap_check_host(ld, entry) && |
sudo_ldap_check_host(ld, entry) && |
/* remember that host matched */ |
/* remember that host matched */ |
(ldap_host_matches = -1) && |
(ldap_host_matches = TRUE) && |
/* add matches for listing later */ |
/* verify runas match */ |
sudo_ldap_add_match(ld, entry, pwflag) && |
sudo_ldap_check_runas(ld, entry) && |
/* verify command match */ |
/* verify command match */ |
sudo_ldap_check_command(ld, entry, &setenv_implied) && |
(rc = sudo_ldap_check_command(ld, entry, &setenv_implied)) != UNSPEC |
/* verify runas match */ |
) { |
sudo_ldap_check_runas(ld, entry) |
/* We have a match! */ |
) { |
DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1); |
/* We have a match! */ |
matched = TRUE; |
DPRINTF(("Perfect Matched!"), 1); |
if (rc == TRUE) { |
/* pick up any options */ |
/* pick up any options */ |
if (setenv_implied) |
if (setenv_implied) |
def_setenv = TRUE; |
def_setenv = TRUE; |
sudo_ldap_parse_options(ld, entry); |
sudo_ldap_parse_options(ld, entry); |
#ifdef HAVE_SELINUX |
#ifdef HAVE_SELINUX |
/* Set role and type if not specified on command line. */ |
/* Set role and type if not specified on command line. */ |
if (user_role == NULL) |
if (user_role == NULL) |
user_role = def_role; |
user_role = def_role; |
if (user_type == NULL) |
if (user_type == NULL) |
user_type = def_type; |
user_type = def_type; |
#endif /* HAVE_SELINUX */ |
#endif /* HAVE_SELINUX */ |
/* make sure we don't reenter loop */ |
/* make sure we don't reenter loop */ |
ret = VALIDATE_OK; |
SET(ret, VALIDATE_OK); |
/* break from inside for loop */ |
CLR(ret, VALIDATE_NOT_OK); |
break; |
} else { |
|
SET(ret, VALIDATE_NOT_OK); |
|
CLR(ret, VALIDATE_OK); |
|
} |
|
/* break from inside for loop */ |
|
break; |
|
} |
} |
} |
entry = ldap_next_entry(ld, entry); |
|
} |
|
if (result) |
|
ldap_msgfree(result); |
ldap_msgfree(result); |
result = NULL; |
result = NULL; |
|
} |
} |
} |
|
|
sudo_ldap_close(ld); /* shut down connection */ |
done: |
|
|
DPRINTF(("user_matches=%d", ldap_user_matches), 1); |
DPRINTF(("user_matches=%d", ldap_user_matches), 1); |
DPRINTF(("host_matches=%d", ldap_host_matches), 1); |
DPRINTF(("host_matches=%d", ldap_host_matches), 1); |
|
|
/* Check for special case for -v, -k, -l options */ |
if (!ISSET(ret, VALIDATE_OK)) { |
if (pwflag && ldap_user_matches && ldap_host_matches) { |
|
/* |
|
* Handle verifypw & listpw |
|
* |
|
* To be extra paranoid, since we haven't read any NOPASSWD options |
|
* in /etc/sudoers yet, but we have to make the decission now, lets |
|
* assume the worst and prefer to prompt for password unless the setting |
|
* is "never". (example verifypw=never or listpw=never) |
|
* |
|
*/ |
|
ret = VALIDATE_OK; |
|
if (pwflag == -1) { |
|
SET(ret, FLAG_NOPASS); /* -k or -K */ |
|
} else { |
|
switch (sudo_defs_table[pwflag].sd_un.tuple) { |
|
case never: |
|
SET(ret, FLAG_NOPASS); |
|
break; |
|
case always: |
|
if (def_authenticate) |
|
SET(ret, FLAG_CHECK_USER); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
} |
|
if (ISSET(ret, VALIDATE_OK)) { |
|
/* we have a match, should we check the password? */ |
|
if (!def_authenticate) |
|
SET(ret, FLAG_NOPASS); |
|
if (def_noexec) |
|
SET(ret, FLAG_NOEXEC); |
|
if (def_setenv) |
|
SET(ret, FLAG_SETENV); |
|
} else { |
|
/* we do not have a match */ |
/* we do not have a match */ |
ret = VALIDATE_NOT_OK; |
if (pwflag && list_pw == NULL) |
if (pwflag) |
|
SET(ret, FLAG_NO_CHECK); |
SET(ret, FLAG_NO_CHECK); |
else if (!ldap_user_matches) |
|
SET(ret, FLAG_NO_USER); |
|
else if (!ldap_host_matches) |
|
SET(ret, FLAG_NO_HOST); |
|
} |
} |
DPRINTF(("sudo_ldap_check(%d)=0x%02x", pwflag, ret), 1); |
if (ldap_user_matches) |
|
CLR(ret, FLAG_NO_USER); |
|
if (ldap_host_matches) |
|
CLR(ret, FLAG_NO_HOST); |
|
DPRINTF(("sudo_ldap_lookup(%d)=0x%02x", pwflag, ret), 1); |
|
|
return(ret); |
return(ret); |
} |
} |
|
|
/* |
/* |
* shut down LDAP connection |
* shut down LDAP connection |
*/ |
*/ |
static void |
int |
sudo_ldap_close(LDAP *ld) |
sudo_ldap_close(nss) |
|
struct sudo_nss *nss; |
{ |
{ |
if (ld) |
if (nss->handle != NULL) { |
ldap_unbind_s(ld); |
ldap_unbind_ext_s((LDAP *) nss->handle, NULL, NULL); |
|
nss->handle = NULL; |
|
} |
|
return(0); |
|
} |
|
|
|
/* |
|
* STUB |
|
*/ |
|
int |
|
sudo_ldap_parse(nss) |
|
struct sudo_nss *nss; |
|
{ |
|
return(0); |
} |
} |