#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <ldap.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <grp.h>
#include "ad_func.h"
void
zero_string(char *line, int len)
{
int x=0;
for(;x<len;) line[x++]='\0';
}
void
strtolower(char *line, int len)
{
int x=0;
for(;line[x]!='\0' && x<=len;x++) if(line[x]>=65 && line[x]<=90)line[x]+=32;
return;
}
/*begin control file read functions*/
int
read_int(const char *filename)
{
char line[CHAR_MAX];
int x, t, fp;
if ((fp = open(filename, O_RDONLY)) == -1) return 0;
zero_string(line, sizeof(line));
t=read(fp, line, CHAR_MAX);
close(fp);
for(x=0;x<=t && x<CHAR_MAX;x++) if(!(line[x]>=48&&line[x]<=57)) break;
line[x]='\0';
return atoi(line);
}
char *
read_line(const char *filename)
{
int x, t, fp;
char *line;
if ((fp = open(filename, O_RDONLY)) == -1) return NULL;
line=(char *)malloc(CHAR_MAX);
zero_string(line, CHAR_MAX);
t=read(fp, line, CHAR_MAX);
close(fp);
for(x=0;x<=t && x<CHAR_MAX;x++) if(line[x]=='\r' || line[x]=='\n') break;
line[x]='\0';
return line;
}
ad_pwd *
getdefault_mailuser(const char *mailname)
{
static ad_pwd ad;
char t[CHAR_MAX];
if((ad.uid=read_int(AD_DEFAULTUID_LOC))==0)
{
syslog(AD_LOG_TO | LOG_ERR, "Could not retrieve value from file: %s", AD_DEFAULTUID_LOC);
return &ad;
}
if((ad.gid=read_int(AD_DEFAULTGID_LOC))==0)
{
syslog(AD_LOG_TO | LOG_ERR, "Could not retrieve value from file: %s", AD_DEFAULTGID_LOC);
return &ad;
}
if((ad.home_dir=read_line(AD_DEFAULTMAILROOT_LOC))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "Could not retrieve value from file: %s", AD_DEFAULTMAILROOT_LOC);
return &ad;
}
if(strlen(mailname)+strlen(ad.home_dir)+1>CHAR_MAX)
{
syslog(AD_LOG_TO | LOG_ERR, "Out of bounds error: %s+%s", ad.home_dir, mailname);
return &ad;
}
if(CONVERT_MAILNAME_TO_LOWERCASE)
{
strcpy(t,mailname);
strtolower(t, CHAR_MAX);
strcat(ad.home_dir, t);
}else strcat(ad.home_dir, mailname);
if((ad.shell=read_line(AD_DEFAULTSHELL_LOC))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "Could not retrieve value from file: %s", AD_DEFAULTSHELL_LOC);
return &ad;
}
if(mailname==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "UserName is null");
return &ad;
}
ad.user=(char *)malloc(CHAR_MAX);
strcpy(ad.user, mailname);
ad.is_complete=1;
return &ad;
}
ad_defs *
get_ad_defaults(void)
{
static ad_defs ad;
if((ad.server=read_line(AD_DEFAULT_ADSERVERNAME_LOC))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "get_ad_defaults: Could not retrieve value from file: %s", AD_DEFAULT_ADSERVERNAME_LOC);
return &ad;
}
if((ad.general_user=read_line(AD_DEFAULT_ADGENERALUSERDN_LOC))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "get_ad_defaults: Could not retrieve value from file: %s", AD_DEFAULT_ADGENERALUSERDN_LOC);
return &ad;
}
if((ad.general_pw=read_line(AD_DEFAULT_ADGENERALUSERPASS_LOC))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "get_ad_defaults: Could not retrieve value from file: %s", AD_DEFAULT_ADGENERALUSERPASS_LOC);
return &ad;
}
if((ad.basedn=read_line(AD_DEFAULT_ADBASEDN_LOC))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "get_ad_defaults: Could not retrieve value from file: %s", AD_DEFAULT_ADBASEDN_LOC);
return &ad;
}
if((ad.domain=read_line(AD_DEFAULT_ADDOMAIN_LOC))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "get_ad_defaults: Could not retrieve value from file: %s", AD_DEFAULT_ADDOMAIN_LOC);
return &ad;
}
ad.is_complete=1;
return &ad;
}
/*end control file read functions*/
/*begin user/group setting functions*/
int
setup_identity(ad_pwd *ad)
{
gid_t ga[1];
char t[CHAR_MAX];
ga[0]=(gid_t)ad->gid;
if(setgroups(1, ga)==-1) return 0;
if(setgid((gid_t) ad->gid)!=0) return 0;
if(setuid((uid_t) ad->uid)!=0) return 0;
if(chdir(ad->home_dir)!=0)
{
if(CREATE_HOME_DIR)
{
if(mkdir(ad->home_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)!=0) return 0;
syslog(AD_LOG_TO | LOG_WARNING, "Created homedir at: %s", ad->home_dir);
if(CREATE_MAILDIR)
{
if(strlen(QMAIL_MAILDIRMAKE) + strlen(ad->home_dir) + 10 > CHAR_MAX) return 0;
sprintf(t, "%s %s/Maildir", QMAIL_MAILDIRMAKE, ad->home_dir);
if(system(t)!=0) return 0;
syslog(AD_LOG_TO | LOG_WARNING, "Created maildir in: %s", ad->home_dir);
if(chdir(ad->home_dir)!=0) return 0;
if(system("echo \"./Maildir/\">.qmail")!=0) return 0;
syslog(AD_LOG_TO | LOG_WARNING, "Created .qmail file in: %s", ad->home_dir);
}
syslog(AD_LOG_TO | LOG_WARNING, "File and home directory creation complete: %s", ad->home_dir);
} else return 0;
}
if(setenv("USER", ad->user, 1)!=0) return 0;
if(setenv("HOME", ad->home_dir, 1)!=0) return 0;
if(setenv("SHELL", ad->shell, 1)!=0) return 0;
return 1;
}
/*end user/group setting functions*/
/*begin LDAP Active Directory functions*/
int
ad_setoption(LDAP *ad_conn, int isVersion3)
{
int version;
if(isVersion3 != 1) {
version = LDAP_VERSION2;
if(ldap_set_option(ad_conn, LDAP_OPT_PROTOCOL_VERSION, &version)!=LDAP_OPT_SUCCESS) return AD_ERROR;
else return AD_SUCCESS;
}else{
version = LDAP_VERSION3;
if(ldap_set_option(ad_conn, LDAP_OPT_PROTOCOL_VERSION, &version)!=LDAP_OPT_SUCCESS) return AD_ERROR;
else return AD_SUCCESS;
}
}
LDAP *
ad_init(const char *server, int useGC, int useSSL)
{
LDAP *ad_conn;
char istring[CHAR_MAX];
/* allocate the connection */
if(strlen(server)>CHAR_MAX-15)
{
syslog(AD_LOG_TO | LOG_ERR, "ad_init: Overflow error: ldap%s://%s:%d", (useSSL?"s":""), server, (useGC==1?GC_SSL_PORT:LDAPS_PORT));
return NULL;
}
sprintf(istring, "ldap%s://%s:%d", (useSSL?"s":""), server, (useGC==1?(useSSL?GC_SSL_PORT:GC_PORT):(useSSL?LDAPS_PORT:LDAP_PORT)));
if(ldap_initialize(&ad_conn, istring)!=LDAP_SUCCESS)
{
syslog(AD_LOG_TO | LOG_ERR, "ad_init: Could not initialize %s %sconnection to AD: %s", (useGC==1?"GC":"LDAP"), (useSSL?"SSL ":""), server);
return NULL;
}
if(ad_setoption(ad_conn, 1)!=AD_SUCCESS)
{
syslog(AD_LOG_TO | LOG_ERR, "ad_init: Could not set LDAP options");
return NULL;
}
return ad_conn;
}
void
ad_close(LDAP *ad_conn)
{
ldap_unbind_s(ad_conn);
}
int
ad_bind(LDAP *ad_conn, const char *binddn, const char *passwd)
{
int attempt=0, res;
/* connect to the LDAP server */
for(;attempt<AD_MAX_BIND_ATTEMPT;attempt++)
if((res=ldap_simple_bind_s(ad_conn, binddn, passwd))==LDAP_SUCCESS) return AD_SUCCESS;
syslog(AD_LOG_TO | LOG_ERR, "AD Authentication failed with dn: %s - %s", binddn, ldap_err2string(res));
return AD_ERROR;
}
int
ad_getsam_fulldn(LDAP *ad_conn, const char *base, const char *domain, const char *username, char *samdn)
{
LDAPMessage *msg, *fe;
char filter[CHAR_MAX];
char *attr[]= {AD_USERNAME_FIELD, (char *)0};
struct timeval timeout;
char *dn;
int res;
timeout.tv_sec = AD_TIMEOUT_SEC;
timeout.tv_usec = 0;
if(strlen(username)+strlen(domain)+32>CHAR_MAX)
{
syslog(AD_LOG_TO | LOG_ERR, "ad_getsam_fulldn: Email address string out of bounds error: username=%s, domain=%s", username, domain);
return AD_ERROR;
}
sprintf(filter, "(&(objectClass=User)(mail=%s%s%s))", username, (ALLOW_WILDCARDS_IN_EMAIL_ADDRESS?"@*":"@"), domain);
if(ldap_search_st(ad_conn, base, LDAP_SCOPE_SUBTREE, filter, attr, 0, &timeout, &msg)!=LDAP_SUCCESS)
{
syslog(AD_LOG_TO | LOG_ERR, "LDAP could not complete search");
return AD_ERROR;
}
if((res=ldap_count_entries(ad_conn, msg))!=1)
{
if(res==0)
{
syslog(AD_LOG_TO | LOG_ERR, "LDAP could find no match for the email address: %s%s%s", username, (ALLOW_WILDCARDS_IN_EMAIL_ADDRESS?"@*":"@"), domain);
return AD_ERROR;
}else{
syslog(AD_LOG_TO | LOG_ERR, "LDAP returned an invalid number of entries for %s%s%s (%d)", username, (ALLOW_WILDCARDS_IN_EMAIL_ADDRESS?"@*":"@"), domain, ldap_count_entries(ad_conn, msg));
return AD_ERROR;
}
}
if((fe=ldap_first_entry(ad_conn, msg))==NULL)
{
syslog(AD_LOG_TO | LOG_ERR, "Could not retrieve result entry");
return AD_ERROR;
}
if((dn=ldap_get_dn(ad_conn, fe))==NULL){
syslog(AD_LOG_TO | LOG_ERR, "LDAP returned null for the dn");
return AD_ERROR;
}
if(strlen(dn)>=CHAR_MAX){
syslog(AD_LOG_TO | LOG_ERR, "DN exceeded CHAR_MAX length of %d", CHAR_MAX);
return AD_ERROR;
}
strncpy(samdn, dn, CHAR_MAX);
samdn[CHAR_MAX-1]='\0';
return AD_SUCCESS;
}
int
ad_verify_user(const char *user, const char *pass)
{
LDAP * ld;
char sam[512];
ad_defs *ad=get_ad_defaults();
if(!ad->is_complete) return AD_ERROR;
if((ld=ad_init(ad->server, AD_USE_GC, AD_ONLY_USE_SSL))==0) return AD_ERROR;
if(ad_bind(ld, ad->general_user, ad->general_pw)==AD_SUCCESS)
{
if((ad_getsam_fulldn(ld, ad->basedn, ad->domain, user, sam))==AD_SUCCESS)
{
ad_close(ld);
/*Now that we have the user, lets rebind with them to verify the password*/
if((ld=ad_init(ad->server, AD_USE_GC, AD_ONLY_USE_SSL))!=0)
{
if(ad_bind(ld, sam, pass)==AD_SUCCESS)
{
ad_close(ld);
return AD_SUCCESS;
}
}
}
}
ad_close(ld);
return AD_ERROR;
}
int
ad_getpw(const char *user)
{
LDAP * ld;
ad_pwd *adp;
ad_defs *ad=get_ad_defaults();
char ts[CHAR_MAX], output[CHAR_MAX];
const char *t=ts;
int x=0, y=0, z=0;
if(!ad->is_complete) return AD_ERROR;
if((ld=ad_init(ad->server, AD_USE_GC, AD_ONLY_USE_SSL))==0) return AD_ERROR;
if(ad_bind(ld, ad->general_user, ad->general_pw)!=AD_SUCCESS)
{
ad_close(ld);
return AD_ERROR;
}
if((ad_getsam_fulldn(ld, ad->basedn, ad->domain, user, ts))!=AD_SUCCESS)
{
ad_close(ld);
return AD_ERROR;
}
ad_close(ld);
adp=getdefault_mailuser(user);
if(!ad->is_complete) return AD_ERROR;
for(z=0;z<=5;z++)
{
switch(z)
{
case 0:
t=user;
break;
case 1:
sprintf(ts, "%d", adp->uid);
t=ts;
break;
case 2:
sprintf(ts, "%d", adp->gid);
t=ts;
break;
case 3:
t=adp->home_dir;
break;
case 4:
ts[0]='\0';
t=ts;
case 5:
break;
}
for(y=0;t[y]!='\0' && x<CHAR_MAX-1;) output[x++]=t[y++];
output[x++]='\0';
}
y=x;
for(x=0;x<y;x++) putchar(output[x]);
return AD_SUCCESS;
};
int
send_alias(const char *alias)
{
char ts[CHAR_MAX], output[CHAR_MAX];
char *t=ts;
int x=0, y=0, z=0;
for(;z<=5;z++)
{
switch(z)
{
case 0:
t=ALIAS_NAME;
break;
case 1:
sprintf(ts, "%d", ALIAS_UID);
t=ts;
break;
case 2:
sprintf(ts, "%d", ALIAS_GID);
t=ts;
break;
case 3:
t=ALIAS_DIR_HEADER;
break;
case 4:
ts[0]='-';
ts[1]='\0';
t=ts;
break;
case 5:
strcpy(ts, alias);
t=ts;
break;
}
for(y=0;t[y]!='\0' && x<CHAR_MAX-1;) output[x++]=t[y++];
output[x++]='\0';
}
y=x;
for(x=0;x<y;x++) putchar(output[x]);
return AD_SUCCESS;
};
/*end LDAP Active Directory functions*/
syntax highlighted by Code2HTML, v. 0.9.1