/*

  group_ldap_auth: user and group authentication via ldap for squid proxy server

  Author: Tobias Crawley
          tocrawle@users.sourceforge.net
	  http://www.fatgut.org/squid/group_ldap_auth/
          
  Based on squid_ldap_auth.c by:
          Glen Newton 
	  glen.newton@nrc.ca
          Advanced Services 
          CISTI
	  National Research Council

  Usage: group_ldap_auth <search_base> <ldap_server_name> [<ldap_port>]

  Dependencies: You need to get the OpenLDAP libraries
          from http://www.openldap.org

  License: group_ldap_auth is free software; you can redistribute it 
           and/or modify it under the terms of the GNU General Public License 
	   as published by the Free Software Foundation; either version 2, 
	   or (at your option) any later version.
 */

#include <stdio.h>
#include <string.h>
#include <lber.h>
#include <ldap_cdefs.h>
#include <ldap.h>

struct _grouplist
{
    char type;
    char *group;
    struct _grouplist *next;
};

typedef struct _grouplist grouplist;

int checkLDAP(LDAP * ld, char *userid, char *password, grouplist * glist,
    char **group, char *searchBase);

//#ifdef USE_LOG
FILE *logfd = NULL;
//#endif

int
main(int argc, char **argv)
{
    char buf[256];
    char *user, *passwd, *group, *tmpstrptr, *p;
    char *ldapServer;
    int groupcount = 0;
    grouplist *glist = NULL, *currgroup = NULL;
    int ldapPort = LDAP_PORT;
    LDAP *ld;
    char *searchBase;
    int err = 0;
    int i;
//#ifdef USE_LOG
    char logfile[256];
//#endif
    setbuf(stdout, NULL);

    if (argc < 3 || argc > 4) {
	fprintf(stderr,
	    "Usage: squid_ldap_auth search_base ldap_server_name [ldap_server_port]\n");
	exit(1);
    }
//#ifdef USE_LOG
    /* setup a log file */
    sprintf(logfile, "/usr/local/squid/logs/squid_ldap_auth_log.%d", (int) getpid());
    logfd = fopen(logfile, "w");
//#endif

    searchBase = (char *) argv[1];
    ldapServer = (char *) argv[2];
    if (argc == 4) {
	ldapPort = atoi((char *) argv[3]);
    }

    if ((ld = ldap_open(ldapServer, ldapPort)) == NULL) {
	fprintf(stderr, "\nUnable to connect to LDAP server:%s port:%d\n",
	    ldapServer, ldapPort);
	exit(1);
    }
    ldap_simple_bind_s(ld, "", "");
//#ifdef USE_LOG
    fprintf(logfd, "bound to %s:%d\n", ldapServer, ldapPort);
//#endif

    while (fgets(buf, 256, stdin) != NULL) {
//#ifdef USE_LOG
	// fprintf(logfd, "received %s", buf);
//#endif

	if ((p = strchr(buf, '\n')) != NULL)
	    *p = '\0';		/* strip \n */

	if ((user = strtok(buf, " ")) == NULL) {
	    printf("f\n");
	    continue;
	}

	/* Modification CJM (Louis-Steve Desjardins) pour enlever le mot de
	   passe du fichier log */

 	fprintf(logfd, "received %s *****\n", user);

	if ((passwd = strtok(NULL, " ")) == NULL) {
	    printf("f\n");
	    continue;
	}
	if ((tmpstrptr = strtok(NULL, " ")) == NULL) {
	    printf("f\n");
	    continue;
	}
	groupcount = atoi(tmpstrptr);
	for (i = 0; i < groupcount; i++) {
	    if (!glist) {
		currgroup = glist = (grouplist *) malloc(sizeof(grouplist));
	    } else {
		currgroup->next = (grouplist *) malloc(sizeof(grouplist));
		currgroup = currgroup->next;
	    }
	    currgroup->next = NULL;

	    if ((tmpstrptr = strtok(NULL, " ")) == NULL) {
		printf("f\n");
		continue;
	    }
	    currgroup->type = tmpstrptr[0];

	    if ((currgroup->group = strtok(NULL, "#\0")) == NULL) {
		printf("f\n");
		continue;
	    }
	}
	group = NULL;
	err = checkLDAP(ld, user, passwd, glist, &group, searchBase);
//#ifdef USE_LOG
	fprintf(logfd, "checkLdap returned %d %s\n", err,
	    (group == NULL) ? "" : group);
//#endif

	if (err == 0) {
	    if (group == NULL) {
		printf("p\n");
	    } else {
		printf("p %s\n", group);
	    }
	} else {
	    printf("f\n");
	}

	/* free the glist */
	while (glist != NULL) {
	    currgroup = glist->next;
	    free(glist);
	    glist = currgroup;
	}
	glist = currgroup = NULL;

	ldap_simple_bind_s(ld, "", "");
//#ifdef USE_LOG
	fflush(logfd);
//#endif
    }
//#ifdef USE_LOG
    fclose(logfd);
//#endif
    ldap_unbind(ld);
}



int
checkLDAP(LDAP * ld, char *userid, char *password, grouplist * glist,
    char **group, char *searchBase)
{
    char filter[1024];
    char *attrs[2];
    char *userdn;
    LDAPMessage *result;
    int entryCount = 0;
    char **vals, **prevals;
    char found = 0;
    char *resultdn;
/*    
     Modifie le 9 avril 2001
     Pour fonctionnement avec serveur LDAP/NDS des CJM

    // verify user exists 
    sprintf(filter, "(uid=%s)", userid);
*/

   /* verify user exists */
   /* Modifie le 9 avril 2001 */
   /* Pour fonctionnement avec serveur LDAP/NDS des CJM */

    sprintf(filter, "(cn=%s)", userid); // Modification : Louis-Steve Desjardins

//#ifdef USE_LOG
    fprintf(logfd, "searching for user with filter %s\n", filter);
//#endif
    attrs[0] = NULL;
    if (ldap_search_s(ld, searchBase, LDAP_SCOPE_SUBTREE, filter, attrs, 0,
	    &result) != LDAP_SUCCESS) {
	return 1;
    }

    /* get the user's dn */
    if ((result = ldap_first_entry(ld, result)) == NULL) {
	return 2;
    }
    if ((userdn = ldap_get_dn(ld, result)) == NULL) {
	return 3;
    }

    if (glist != NULL) {

	while (glist != NULL && !found) {
	    if (glist->type == 'd') {

                attrs[0] = NULL;
//#ifdef USE_LOG
		fprintf(logfd,
		    "searching for dynamic group %s\n",
		    glist->group);
//#endif
                if (ldap_search_s(ld, searchBase, LDAP_SCOPE_SUBTREE, glist->group,
			attrs, 0, &result) != LDAP_SUCCESS) {
		    free(userdn);
		    return 4;
		}

                entryCount = ldap_count_entries(ld, result);
            
                if (entryCount > 0) {
                    result = ldap_first_entry(ld, result);
                    while (result != NULL && !found) {
                        resultdn = ldap_get_dn(ld, result);
                        if (!strcmp(userdn, resultdn)) {
                            found = 1;
                            *group = glist->group;
                        }
                        
                        free(resultdn);
                        
                        result = ldap_next_entry(ld, result);
                    
                    }
                }
                
	    } else {
		/* static group */
		sprintf(filter,
		    "(& (cn=%s) (| (objectclass=groupofuniquenames) (objectclass=groupofnames)))",
		    glist->group);
		attrs[0] = strdup("uniquemember");
		attrs[1] = NULL;
//#ifdef USE_LOG
		fprintf(logfd,
		    "searching for static group %s using filter %s\n",
		    glist->group, filter);
//#endif
		if (ldap_search_s(ld, searchBase, LDAP_SCOPE_SUBTREE, filter,
			attrs, 0, &result) != LDAP_SUCCESS) {
		    free(userdn);
		    free(attrs[0]);
		    return 4;
		}
            
            
                entryCount = ldap_count_entries(ld, result);
            
                if (entryCount > 0) {
                    result = ldap_first_entry(ld, result);
                    while (result != NULL && !found) {
                        prevals = vals =
                            ldap_get_values(ld, result, "uniquemember");
                        while (vals != NULL && *vals != NULL && !found) {
                            if (!strcmp(userdn, *vals++)) {
                                found = 1;
                                *group = glist->group;
                            }
                        }
                        ldap_value_free(prevals);
                        
                        result = ldap_next_entry(ld, result);
                    
                    }
                }

                free(attrs[0]);
            }
            

	    
//#ifdef USE_LOG
            if (!found)
                fprintf(logfd, "user %s not found in group %s\n", userdn,
                        glist->group);
            else
                fprintf(logfd, "user %s found in group %s\n", userdn,
			glist->group);
//#endif
	    glist = glist->next;

	}

	if (!found) {
	    free(userdn);
	    return 5;
	}
    }

//#ifdef USE_LOG
    fprintf(logfd, "binding as %s\n", userdn);
//#endif
    if (ldap_simple_bind_s(ld, userdn, password) != LDAP_SUCCESS) {
	free(userdn);
	return 6;
    }

    free(userdn);
    return 0;
}


