
/* Verifica acceso a un sistema en el que se autentica con LDAP.
 * Opcionalmente permite ejecutar un comando con el UID y GID
 * del usuario. Dominio público. 2006.
 * http://structio.sourceforge.net/sigue
 * 
 * Referencias: 
 *      openbsd-local.c
 * 	Fuentes de login_ldap http://www.ifost.org.au 
 * 	man ldap open_ldap ldap_search ldap_bind
 * 	http://www.rzg.mpg.de/vc/docs/apan/software/ldaplink.cxx
 * */

#include <assert.h>
#include <pwd.h>
#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>

/* LDAP */
#include <lber.h>
#include <ldap.h>

#include "cmdldap.h"


static char user[128], pass[128], cmd[1024]; 

void die(void) { 
	memset(user, '\0', 128); 
	memset(pass, '\0', 128); 
} 

/* Lo que se envié a stderr queda en /var/www/logs/error_log */
void run(uid_t uid, gid_t gid) {
	char *s;
	FILE *t;
	char msg[1000];
	time_t ti=time(NULL);
	sprintf(msg, "run: %s uid=%i, gid=%i, cmd='%s'", ctime(&ti), uid, gid, cmd);
	warnx(msg); 
	if (seteuid(uid)!=0) {
		perror("No puede poner uid. ¿Seguro en /etc/fstab no hay nosuid en la partición en la que está cmdldap?");
	}
/* apache debe ser del grupo profesores  	if (setegid(gid)!=0) {
		asprintf(&s,"cannot set gid %i",gid);
		perror(s);
		free(s);
	}  */
//	system("/usr/bin/id");

	if (system(cmd)!=0) {
		perror("Problema ejecutando");
	}
}


/** Función practicamente copiada de login_ldap.
 */
static int
do_simple_bind(LDAP **ld, char *username, char *password)
{
	int msgid, rc, parserc;
	struct timeval timeo;
	LDAPMessage *res;
	
	assert(ld!=NULL);
	assert(username!=NULL);
	assert(password!=NULL);

	timeo.tv_sec=60;
	timeo.tv_usec=0;

	msgid = ldap_simple_bind(*ld, username, password);
	if (msgid == -1) 
		return(0);
	
loop:
	rc = ldap_result(*ld, msgid, 0, &timeo, &res);
	switch(rc) {
		
	case -1:
		ldap_unbind(*ld);
		*ld=NULL;
		return(0);

	case 0:
		goto loop;

	default:
		parserc = ldap_parse_result(*ld, res, &rc, NULL, NULL, NULL, 
		    NULL, 1);	
		if (parserc != LDAP_SUCCESS) 
			return(0);
		
		if (rc != LDAP_SUCCESS) 
			return(0);
	}

	return(1);
}

/** 
 * Autentica al usuario username con clave password en el 
 * servidor con URI uri.  El DN base es basedn, el DN de las personas
 * de la organización es gentedn.  
 * @return Si lo encuentra retorna 1 en uid y gid retorna UID y GID del
 * usuario.  Si no lo encuentra retorna 0. 
 */
int aut_ldap(char *uri, char *basedn, char *gentedn, char *username, 
		char *password, int *uid, int *gid)
{
	LDAP *ld;
	char *filter;
	LDAPMessage *res=NULL;
	struct timeval timeout;
	int rc;
	LDAPMessage *mess=NULL;
	int rv=0;
	char **val=NULL;
	int vers;

	assert(uri!=NULL);
	assert(basedn!=NULL);
	assert(gentedn!=NULL);
	assert(username!=NULL);
	assert(password!=NULL);
	assert(uid!=NULL);
	assert(gid!=NULL);

	*uid=0;
	*gid=0;
	if ((filter=malloc(sizeof(char)*100+strlen(username)))==NULL) {
		warnx("Memoria insuficiente");
		goto sale;
	}
	
	timeout.tv_sec=2;
	timeout.tv_usec=0;
	if ((rc=ldap_initialize(&ld, uri))!=LDAP_SUCCESS) {
		warnx("Falla en ldap_init");
		goto sale;
	}
	vers=LDAP_VERSION3;
	if ((rc=ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &vers))
			!=LDAP_OPT_SUCCESS) {
		warnx("Falla setoption");
		goto sale;
	}

	snprintf(filter, 100+strlen(username), 
		"(&(objectclass=posixAccount)(uid=%s))",username);
	if ((rc=ldap_search_st(ld, basedn, LDAP_SCOPE_SUBTREE, filter, NULL, 
			0, &timeout, &res))<0) {
		warnx("Falla en ldap_search_st");
		goto sale;
	}

	if ((rc=ldap_count_entries(ld, res))<0) {
		warnx("Falla en ldap_count_entries");
		goto sale;
	}

	if (rc==0) {
		warnx("Usuario no existe");
		goto sale;
	}

	if ((mess=ldap_first_entry(ld, res))==NULL) {
		warnx("Falla en ldap_first_entry");
		goto sale;
	}
	
	if ((val=ldap_get_values(ld, mess, "uidNumber"))==NULL) {
		warnx("Falla get_values uid");
		goto sale;
	}
	*uid=atoi((char *)(*val));
	ldap_value_free(val);
	if ((val=ldap_get_values(ld, mess, "gidNumber"))==NULL) {
		warnx("Falla get_values gid");
		goto sale;
	}
	*gid=atoi(*val);
	ldap_value_free(val);
	val=NULL;

	ldap_msgfree(res);
	res=NULL;
	snprintf(filter, 100+strlen(username),
			"uid=%s,%s",
			username, gentedn); 
	if (do_simple_bind(&ld, filter, password)!=1) {
		warnx("Falla en do_simple_bind filter password");
		goto sale;
	}
	rv=1;

sale:
	if (res!=NULL) {
		ldap_msgfree(res);
	}
	if (ld!=NULL) {
		ldap_unbind_s(ld);
	}
	if (filter!=NULL) {
		free(filter);
	}
	if (val!=NULL) {
		ldap_value_free(val);
	}
	return rv;

}


int main(int argc, char *argv[]) {
	int uid, gid;
	atexit(die); die(); 

	if (fscanf(stdin, "%127s %127s", user, pass) != 2) {
		warnx("unexpected input");
		return 1; 
	}

	if (aut_ldap(LDAP_URI, LDAP_BASEDN, LDAP_GENTEDN, user, pass, 
		&uid, &gid))  {
		(void) memset(pass, 0, strlen(pass));
		if (fgets(cmd,1024,stdin)!=NULL) {
			if (cmd[strlen(cmd)-1]=='\n') {
				cmd[strlen(cmd)-1]='\0';
			}
			run(uid,gid);
		}
		return 0;
	}
	warnx("incorrect password");

	return 1; 
} 
