#include "config.h"
#include <sys/types.h>
#include <sys/param.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
#ifdef HAVE_STRING_H
# include <string.h>
#else
# ifdef HAVE_STRINGS_H
# include <strings.h>
# endif
#endif
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#ifdef HAVE_LBER_H
#include <lber.h>
#endif
#include <ldap.h>
#include "sudo.h"
#include "parse.h"
#ifndef lint
static const char rcsid[] = "$Sudo: ldap.c,v 1.14 2004/09/02 04:03:25 aaron Exp $";
#endif
#ifndef BUF_SIZ
#define BUF_SIZ 1024
#endif
#ifndef LDAP_OPT_SUCCESS
#define LDAP_OPT_SUCCESS LDAP_SUCCESS
#endif
extern int printmatches;
struct ldap_config {
char *host;
int port;
int version;
char *uri;
char *binddn;
char *bindpw;
char *base;
char *ssl;
int tls_checkpeer;
char *tls_cacertfile;
char *tls_cacertdir;
char *tls_random_file;
char *tls_cipher_suite;
char *tls_certfile;
char *tls_keyfile;
int debug;
} ldap_conf;
int
sudo_ldap_check_user_netgroup(ld,entry)
LDAP *ld;
LDAPMessage *entry;
{
char **v=NULL;
char **p=NULL;
int ret=0;
if (!entry) return ret;
v=ldap_get_values(ld,entry,"sudoUser");
for (p=v; p && *p && !ret;p++)
{
if (ldap_conf.debug>1) printf("ldap sudoUser netgroup '%s' ...",*p);
if (netgr_matches(*p,NULL,NULL,user_name)) ret=1;
if (ldap_conf.debug>1) printf(" %s\n",ret ? "MATCH!" : "not");
}
if (v) ldap_value_free(v);
return ret;
}
int
sudo_ldap_check_host(ld,entry)
LDAP *ld;
LDAPMessage *entry;
{
char **v=NULL;
char **p=NULL;
int ret=0;
if (!entry) return ret;
v=ldap_get_values(ld,entry,"sudoHost");
for (p=v; p && *p && !ret;p++)
{
if (ldap_conf.debug>1) printf("ldap sudoHost '%s' ...",*p);
if (
!strcasecmp(*p,"ALL") ||
addr_matches(*p) ||
netgr_matches(*p,user_host,user_shost,NULL) ||
!hostname_matches(user_shost,user_host,*p)
)
{
ret=1;
}
if (ldap_conf.debug>1) printf(" %s\n",ret ? "MATCH!" : "not");
}
if (v) ldap_value_free(v);
return ret;
}
int sudo_ldap_check_runas(ld,entry)
LDAP *ld;
LDAPMessage *entry;
{
char **v=NULL;
char **p=NULL;
int ret=0;
if (!entry) return ret;
v=ldap_get_values(ld,entry,"sudoRunAs");
if (!v)
{
ret=!strcasecmp(*user_runas,def_runas_default);
}
for (p=v; p && *p && !ret;p++)
{
if (ldap_conf.debug>1) printf("ldap sudoRunAs '%s' ...",*p);
if (
!strcasecmp(*p,*user_runas) ||
!strcasecmp(*p,"ALL")
)
{
ret = 1;
}
if (ldap_conf.debug>1) printf(" %s\n",ret ? "MATCH!" : "not");
}
if (v) ldap_value_free(v);
return ret;
}
int sudo_ldap_check_command(ld,entry)
LDAP *ld;
LDAPMessage *entry;
{
char **v=NULL;
char **p=NULL;
char *allowed_cmnd;
char *allowed_args;
int ret=0;
int foundbang;
if (!entry) return ret;
v=ldap_get_values(ld,entry,"sudoCommand");
for (p=v; p && *p && ret>=0;p++){
if (ldap_conf.debug>1) printf("ldap sudoCommand '%s' ...",*p);
if (!strcasecmp(*p,"ALL")) {
ret=1;
if (ldap_conf.debug>1) printf(" MATCH!\n");
continue;
}
if (**p != '!'){
foundbang=0;
allowed_cmnd=estrdup(*p);
} else {
foundbang=1;
allowed_cmnd=estrdup(1+*p);
}
allowed_args=strchr(allowed_cmnd,' ');
if (allowed_args) *allowed_args++='\0';
if (command_matches(allowed_cmnd,allowed_args)) {
if (!foundbang){
ret=1;
} else {
ret=-1;
}
if (ldap_conf.debug>1) printf(" MATCH!\n");
} else {
if (ldap_conf.debug>1) printf(" not\n");
}
free(allowed_cmnd);
}
if (v) ldap_value_free(v);
return ret > 0;
}
void
sudo_ldap_parse_options(ld,entry)
LDAP *ld;
LDAPMessage *entry;
{
char **v=NULL;
char **p=NULL;
char *var;
char *val;
char op;
if (!entry) return;
v=ldap_get_values(ld,entry,"sudoOption");
for (p=v; p && *p;p++){
if (ldap_conf.debug>1) printf("ldap sudoOption: '%s'\n",*p);
var=estrdup(*p);
val=strchr(var,'=');
if (val>var){
*val++='\0';
op=*(val-2);
if (op == '+' || op == '-') {
*(val-2)='\0';
set_default(var,val,(int)op);
} else {
set_default(var,val,TRUE);
}
} else if (*var=='!'){
set_default(var+1,NULL,FALSE);
} else {
set_default(var,NULL,TRUE);
}
free(var);
}
if (v) ldap_value_free(v);
}
void
ncat(s,sz,src)
char **s;
size_t *sz;
char *src;
{
size_t nsz;
if (*s == NULL){
*s=estrdup(src);
*sz=strlen(src)+1;
return;
}
nsz= strlen(*s) + strlen(src) + 1;
if (*sz < nsz) *s=erealloc( (void *)*s , *sz=nsz*2);
strlcat(*s,src,*sz);
}
char *
sudo_ldap_build_pass1()
{
struct group *grp;
gid_t *grplist=NULL;
int ngrps;
int i;
char *b=NULL;
size_t sz;
ncat(&b,&sz,"(|");
ncat(&b,&sz,"(sudoUser=");
ncat(&b,&sz,user_name);
ncat(&b,&sz,")");
grp=getgrgid(getgid());
if (grp!=NULL){
ncat(&b,&sz,"(sudoUser=%");
ncat(&b,&sz,grp->gr_name);
ncat(&b,&sz,")");
}
if (0<(ngrps=getgroups(0,NULL))){
grplist=calloc(ngrps,sizeof(gid_t));
if (grplist!=NULL && (0<getgroups(ngrps,grplist)))
for(i=0;i<ngrps;i++){
if((grp=getgrgid(grplist[i]))!=NULL){
ncat(&b,&sz,"(sudoUser=%");
ncat(&b,&sz,grp->gr_name);
ncat(&b,&sz,")");
}
}
}
ncat(&b,&sz,"(sudoUser=ALL)");
ncat(&b,&sz,")");
return b ;
}
int
_atobool(s)
char *s;
{
if (!strcasecmp(s,"yes") || !strcasecmp(s,"true") || !strcasecmp(s,"on"))
return 1;
if (!strcasecmp(s,"no") || !strcasecmp(s,"false") || !strcasecmp(s,"off"))
return 0;
return -1;
}
int
sudo_ldap_read_config()
{
FILE *f;
char buf[BUF_SIZ];
char *c;
char *keyword;
char *value;
ldap_conf.tls_checkpeer=-1;
f=fopen(_PATH_LDAP_CONF,"r");
if (!f) return 0;
while (f && fgets(buf,sizeof(buf)-1,f)){
c=buf;
if (*c == '#') continue;
if (*c == '\n') continue;
if (!*c) continue;
while (isspace(*c)) c++;
keyword=c;
while (*c && !isspace(*c)) c++;
if (*c) {
*c='\0';
c++;
}
while (isspace(*c)) c++;
value=c;
while (*c) c++;
while (--c > value && isspace(*c)) *c='\0';
#define MATCH_S(x,y) if (!strcasecmp(keyword,x)) \
{ if (y) free(y); y=estrdup(value); }
#define MATCH_I(x,y) if (!strcasecmp(keyword,x)) { y=atoi(value); }
#define MATCH_B(x,y) if (!strcasecmp(keyword,x)) { y=_atobool(value); }
MATCH_S("host", ldap_conf.host)
else MATCH_I("port", ldap_conf.port)
else MATCH_S("ssl", ldap_conf.ssl)
else MATCH_B("tls_checkpeer", ldap_conf.tls_checkpeer)
else MATCH_S("tls_cacertfile", ldap_conf.tls_cacertfile)
else MATCH_S("tls_cacertdir", ldap_conf.tls_cacertdir)
else MATCH_S("tls_randfile", ldap_conf.tls_random_file)
else MATCH_S("tls_ciphers", ldap_conf.tls_cipher_suite)
else MATCH_S("tls_cert", ldap_conf.tls_certfile)
else MATCH_S("tls_key", ldap_conf.tls_keyfile)
else MATCH_I("ldap_version", ldap_conf.version)
else MATCH_S("uri", ldap_conf.uri)
else MATCH_S("binddn", ldap_conf.binddn)
else MATCH_S("bindpw", ldap_conf.bindpw)
else MATCH_S("sudoers_base", ldap_conf.base)
else MATCH_I("sudoers_debug", ldap_conf.debug)
else {
}
}
if (f) fclose(f);
if (!ldap_conf.version) ldap_conf.version=3;
if (!ldap_conf.port) ldap_conf.port=389;
if (!ldap_conf.host) ldap_conf.host=estrdup("localhost");
if (ldap_conf.debug>1) {
printf("LDAP Config Summary\n");
printf("===================\n");
#ifdef HAVE_LDAP_INITIALIZE
if (ldap_conf.uri){
printf("uri %s\n", ldap_conf.uri);
} else
#endif
{
printf("host %s\n", ldap_conf.host ?
ldap_conf.host : "(NONE)");
printf("port %d\n", ldap_conf.port);
}
printf("ldap_version %d\n", ldap_conf.version);
printf("sudoers_base %s\n", ldap_conf.base ?
ldap_conf.base : "(NONE) <---Sudo will ignore ldap)");
printf("binddn %s\n", ldap_conf.binddn ?
ldap_conf.binddn : "(anonymous)");
printf("bindpw %s\n", ldap_conf.bindpw ?
ldap_conf.bindpw : "(anonymous)");
#ifdef HAVE_LDAP_START_TLS_S
printf("ssl %s\n", ldap_conf.ssl ?
ldap_conf.ssl : "(no)");
#endif
printf("===================\n");
}
if (!ldap_conf.base) return 0;
return 1;
}
char *
_ldap_join_values(sep,v)
char *sep;
char **v;
{
char **p=NULL;
char *b=NULL;
size_t sz=0;
for (p=v; p && *p;p++){
if (p!=v && sep!=NULL) ncat(&b,&sz,sep);
ncat(&b,&sz,*p);
}
if (b[0]=='\0'){
ncat(&b,&sz,"(empty list)");
}
return b;
}
char * sudo_ldap_cm_list=NULL;
size_t sudo_ldap_cm_list_size;
#define SAVE_LIST(x) ncat(&sudo_ldap_cm_list,&sudo_ldap_cm_list_size,(x))
int
sudo_ldap_add_match(ld,entry)
LDAP *ld;
LDAPMessage *entry;
{
char **v=NULL;
char *dn;
char **edn;
if (printmatches != TRUE) return 1;
dn=ldap_get_dn(ld,entry);
edn=dn ? ldap_explode_dn(dn,1) : NULL;
SAVE_LIST("\nLDAP Role: ");
SAVE_LIST((edn && *edn) ? *edn : "UNKNOWN");
SAVE_LIST("\n");
if (dn) ldap_memfree(dn);
if (edn) ldap_value_free(edn);
v=ldap_get_values(ld,entry,"sudoRunAs");
if (v && *v){
SAVE_LIST(" RunAs: (");
SAVE_LIST(_ldap_join_values(", ",v));
SAVE_LIST(")\n");
}
if (v) ldap_value_free(v);
v=ldap_get_values(ld,entry,"sudoCommand");
if (v && *v){
SAVE_LIST(" Commands:\n ");
SAVE_LIST(_ldap_join_values("\n ",v));
SAVE_LIST("\n");
} else {
SAVE_LIST(" Commands: NONE\n");
}
if (v) ldap_value_free(v);
return 0;
}
#undef SAVE_LIST
void
sudo_ldap_list_matches()
{
if (sudo_ldap_cm_list!=NULL) printf("%s",sudo_ldap_cm_list);
}
int
sudo_ldap_check(pwflag)
int pwflag;
{
LDAP *ld=NULL;
LDAPMessage *result=NULL;
LDAPMessage *entry=NULL;
char *filt;
int rc=0;
int ret=0;
int pass=0;
int ldap_user_matches=0;
int ldap_host_matches=0;
if (!sudo_ldap_read_config()) return VALIDATE_ERROR;
#define SET_OPT(opt,optname,val) \
if (ldap_conf.val!=NULL) { \
if (ldap_conf.debug>1) fprintf(stderr, \
"ldap_set_option(LDAP_OPT_%s,\"%s\")\n",optname,ldap_conf.val); \
rc=ldap_set_option(ld,opt,ldap_conf.val); \
if(rc != LDAP_OPT_SUCCESS){ \
fprintf(stderr,"ldap_set_option(LDAP_OPT_%s,\"%s\")=%d: %s\n", \
optname, ldap_conf.val, rc, ldap_err2string(rc)); \
return VALIDATE_ERROR ; \
} \
} \
#define SET_OPTI(opt,optname,val) \
if (ldap_conf.debug>1) fprintf(stderr, \
"ldap_set_option(LDAP_OPT_%s,0x%02x)\n",optname,ldap_conf.val); \
rc=ldap_set_option(ld,opt,&ldap_conf.val); \
if(rc != LDAP_OPT_SUCCESS){ \
fprintf(stderr,"ldap_set_option(LDAP_OPT_%s,0x%02x)=%d: %s\n", \
optname, ldap_conf.val, rc, ldap_err2string(rc)); \
return VALIDATE_ERROR ; \
} \
#ifdef LDAP_OPT_X_TLS_CACERTFILE
SET_OPT(LDAP_OPT_X_TLS_CACERTFILE, "X_TLS_CACERTFILE", tls_cacertfile);
#endif
#ifdef LDAP_OPT_X_TLS_CACERTDIR
SET_OPT(LDAP_OPT_X_TLS_CACERTDIR, "X_TLS_CACERTDIR", tls_cacertdir);
#endif
#ifdef LDAP_OPT_X_TLS_CERTFILE
SET_OPT(LDAP_OPT_X_TLS_CERTFILE, "X_TLS_CERTFILE", tls_certfile);
#endif
#ifdef LDAP_OPT_X_TLS_KEYFILE
SET_OPT(LDAP_OPT_X_TLS_KEYFILE, "X_TLS_KEYFILE", tls_keyfile);
#endif
#ifdef LDAP_OPT_X_TLS_CIPHER_SUITE
SET_OPT(LDAP_OPT_X_TLS_CIPHER_SUITE, "X_TLS_CIPHER_SUITE", tls_cipher_suite);
#endif
#ifdef LDAP_OPT_X_TLS_RANDOM_FILE
SET_OPT(LDAP_OPT_X_TLS_RANDOM_FILE, "X_TLS_RANDOM_FILE", tls_random_file);
#endif
#ifdef LDAP_OPT_X_TLS_REQUIRE_CERT
if (ldap_conf.tls_checkpeer!=-1){
SET_OPTI(LDAP_OPT_X_TLS_REQUIRE_CERT,"X_TLS_REQUIRE_CERT",tls_checkpeer);
}
#endif
#ifdef HAVE_LDAP_INITIALIZE
if (ldap_conf.uri) {
if (ldap_conf.debug>1) fprintf(stderr,
"ldap_initialize(ld,%s)\n",ldap_conf.uri);
rc=ldap_initialize(&ld,ldap_conf.uri);
if(rc){
fprintf(stderr, "ldap_initialize()=%d : %s\n",
rc,ldap_err2string(rc));
return VALIDATE_ERROR;
}
} else
#endif
if (ldap_conf.host) {
if (ldap_conf.debug>1) fprintf(stderr,
"ldap_init(%s,%d)\n",ldap_conf.host,ldap_conf.port);
ld=ldap_init(ldap_conf.host,ldap_conf.port);
if (!ld) {
fprintf(stderr, "ldap_init(): errno=%d : %s\n",
errno, strerror(errno));
return VALIDATE_ERROR;
}
}
#ifdef LDAP_OPT_PROTOCOL_VERSION
SET_OPTI(LDAP_OPT_PROTOCOL_VERSION,"PROTOCOL_VERSION", version);
#endif
#ifdef HAVE_LDAP_START_TLS_S
if (ldap_conf.ssl && !strcasecmp(ldap_conf.ssl, "start_tls")){
rc = ldap_start_tls_s(ld, NULL, NULL);
if (rc != LDAP_SUCCESS) {
fprintf(stderr, "ldap_start_tls_s(): %d: %s\n", rc, ldap_err2string(rc));
ldap_unbind(ld);
return VALIDATE_ERROR;
}
if (ldap_conf.debug) printf("ldap_start_tls_s() ok\n");
}
#endif
rc=ldap_simple_bind_s(ld,ldap_conf.binddn,ldap_conf.bindpw);
if(rc){
fprintf(stderr,"ldap_simple_bind_s()=%d : %s\n",
rc, ldap_err2string(rc));
return VALIDATE_ERROR ;
}
if (ldap_conf.debug) printf("ldap_bind() ok\n");
rc=ldap_search_s(ld,ldap_conf.base,LDAP_SCOPE_ONELEVEL,
"cn=defaults",NULL,0,&result);
if (!rc && (entry=ldap_first_entry(ld,result))){
if (ldap_conf.debug) printf("found:%s\n",ldap_get_dn(ld,entry));
sudo_ldap_parse_options(ld,entry);
} else {
if (ldap_conf.debug) printf("no default options found!\n");
}
if (result) ldap_msgfree(result);
result=NULL;
for(pass=1;!ret && pass<=2;pass++){
if (pass==1) {
filt=sudo_ldap_build_pass1();
} else {
filt=strdup("sudoUser=+*");
}
if (ldap_conf.debug) printf("ldap search '%s'\n",filt);
rc=ldap_search_s(ld,ldap_conf.base,LDAP_SCOPE_ONELEVEL,
filt,NULL,0,&result);
if (rc) {
if (ldap_conf.debug) printf("nothing found for '%s'\n",filt);
}
if (filt) free (filt);
for(
entry=rc ? NULL : ldap_first_entry(ld,result);
entry!=NULL;
entry=ldap_next_entry(ld,entry))
{
if (ldap_conf.debug) printf("found:%s\n",ldap_get_dn(ld,entry));
if (
(pass!=2 || sudo_ldap_check_user_netgroup(ld,entry)) &&
(ldap_user_matches=-1) &&
sudo_ldap_check_host(ld,entry) &&
(ldap_host_matches=-1) &&
sudo_ldap_add_match(ld,entry) &&
sudo_ldap_check_command(ld,entry) &&
sudo_ldap_check_runas(ld,entry)
)
{
if(ldap_conf.debug) printf("Perfect Matched!\n");
sudo_ldap_parse_options(ld,entry);
ret=VALIDATE_OK;
break;
}
}
if (result) ldap_msgfree(result);
result=NULL;
}
if (ld) ldap_unbind_s(ld);
if (ldap_conf.debug) printf("user_matches=%d\n",ldap_user_matches);
if (ldap_conf.debug) printf("host_matches=%d\n",ldap_host_matches);
if (pwflag && ldap_user_matches && ldap_host_matches){
if (pwflag<0) {
ret=VALIDATE_OK; SET(ret,FLAG_NOPASS);
} else if (sudo_defs_table[pwflag].sd_un.tuple == never){
ret=VALIDATE_OK; SET(ret,FLAG_NOPASS);
} else {
ret=VALIDATE_OK;
}
}
if (ISSET(ret,VALIDATE_OK)) {
if (!def_authenticate) SET(ret,FLAG_NOPASS);
if (def_noexec) SET(ret,FLAG_NOEXEC);
} else {
ret=VALIDATE_NOT_OK;
if (!ldap_user_matches) SET(ret,FLAG_NO_USER);
else if (!ldap_host_matches) SET(ret,FLAG_NO_HOST);
}
if (ldap_conf.debug) printf("sudo_ldap_check(%d)=0x%02x\n",pwflag,ret);
return ret ;
}