This patch adds an execusersearch directive to source definitions, specifying a program to run queryng if a user is part of the source or not. The user name will be suppled as the last argument to the program, after any arguments specified here. Additionally the earlier ldapcachetime global directive is renamed to usercachetime and shared by both LDAP and exec sources. http://www.henriknordstrom.net/code/squidGuard/ diff -ru --exclude doc squidGuard-1.3-ports/src/sgDiv.c.in squidGuard-1.3-hno/src/sgDiv.c.in --- squidGuard-1.3-ports/src/sgDiv.c.in 2007-11-03 14:59:49.000000000 +0100 +++ squidGuard-1.3-hno/src/sgDiv.c.in 2008-12-21 13:41:22.000000000 +0100 @@ -844,7 +844,7 @@ } #if __STDC__ -struct UserInfo *setuserinfo() +struct UserInfo *setuserinfo(void) #else struct UserInfo *setuserinfo() #endif diff -ru --exclude doc squidGuard-1.3-ports/src/sgEscape.c squidGuard-1.3-hno/src/sgEscape.c --- squidGuard-1.3-ports/src/sgEscape.c 2007-05-10 17:39:44.000000000 +0200 +++ squidGuard-1.3-hno/src/sgEscape.c 2008-12-17 19:49:41.000000000 +0100 @@ -127,6 +127,11 @@ sgFree(src->ldapurls); #endif + for(i = 0; i < src->queryprogramcount; i++) { + sgFree(src->queryprograms[i]); + } + sgFree(src->queryprograms); + FREE_LIST(Ip, src->ip, sgFreeIp) /* and finally, the object itself */ diff -ru --exclude doc squidGuard-1.3-ports/src/sgFree.c squidGuard-1.3-hno/src/sgFree.c --- squidGuard-1.3-ports/src/sgFree.c 2007-11-03 14:59:49.000000000 +0100 +++ squidGuard-1.3-hno/src/sgFree.c 2008-12-17 19:48:48.000000000 +0100 @@ -127,6 +127,11 @@ sgFree(src->ldapurls); #endif + for(i = 0; i < src->queryprogramcount; i++) { + sgFree(src->queryprograms[i]); + } + sgFree(src->queryprograms); + FREE_LIST(Ip, src->ip, sgFreeIp) /* and finally, the object itself */ diff -ru --exclude doc squidGuard-1.3-ports/src/sg.h.in squidGuard-1.3-hno/src/sg.h.in --- squidGuard-1.3-ports/src/sg.h.in 2007-11-03 14:59:49.000000000 +0100 +++ squidGuard-1.3-hno/src/sg.h.in 2008-12-21 13:40:50.000000000 +0100 @@ -35,6 +35,7 @@ #include #include #include +#include #include "config.h" #include "version.h" #if __STDC__ @@ -143,12 +144,10 @@ time_t last; int consumed; char status; -#ifdef HAVE_LIBLDAP /* LDAP tracking */ int ldapuser; /* bool: 1 if user loaded from LDAP */ int found; /* bool: we also cache if not found in LDAP */ time_t cachetime; /* time this item was added to cache */ -#endif }; struct UserQuota { @@ -280,6 +279,8 @@ char **ldapurls; /* dynamic array of url strings */ int ldapurlcount; /* current size of pointer array */ #endif + char ***queryprograms; /* dynamic array of argument lists */ + int queryprogramcount; /* current size of pointer array */ struct Source *next; }; @@ -341,6 +342,7 @@ #ifdef HAVE_LIBLDAP void sgSourceLdapUserSearch __P((char *)); #endif +void sgSourceExecUserSearch __P((char *)); void sgSourceExecUserList __P((char *)); void sgSourceDomain __P((char *)); void sgSourceIpList __P((char *)); @@ -440,6 +442,8 @@ #ifdef HAVE_LIBLDAP int sgDoLdapSearch __P((const char *, const char *)); #endif +int sgDoQueryProgram __P((char **, const char *)); +struct UserInfo *setuserinfo __P((void)); int expand_url __P((char *, size_t, const char *, const char *)); diff -ru --exclude doc squidGuard-1.3-ports/src/sg.l squidGuard-1.3-hno/src/sg.l --- squidGuard-1.3-ports/src/sg.l 2007-11-03 14:59:49.000000000 +0100 +++ squidGuard-1.3-hno/src/sg.l 2008-12-21 13:23:08.000000000 +0100 @@ -105,7 +105,8 @@ ^acl return ACL; ^dbhome return DBHOME; ^logdir return LOGDIR; -^ldapcachetime return LDAPCACHETIME; +^ldapcachetime return USERCACHETIME; +^usercachetime return USERCACHETIME; ^ldapprotover return LDAPPROTOVER; ^ldapbinddn { BEGIN LDAPDN_STATE; return LDAPBINDDN; } ^ldapbindpass return LDAPBINDPASS; @@ -120,6 +121,10 @@ BEGIN REDIRECT_STATE; return LDAPUSERSEARCH; } +execusersearch { + BEGIN EXEC_STATE; + return EXECUSERSEARCH; + } execuserlist { BEGIN EXEC_STATE; return EXECUSERLIST; diff -ru --exclude doc squidGuard-1.3-ports/src/sg.y.in squidGuard-1.3-hno/src/sg.y.in --- squidGuard-1.3-ports/src/sg.y.in 2007-11-03 14:59:49.000000000 +0100 +++ squidGuard-1.3-hno/src/sg.y.in 2008-12-21 13:37:51.000000000 +0100 @@ -85,10 +85,10 @@ %token DESTINATION REWRITE ACL TIME TVAL DVAL DVALCRON %token SOURCE CIDR IPCLASS CONTINUE %token IPADDR DBHOME DOMAINLIST URLLIST EXPRESSIONLIST IPLIST -%token DOMAIN USER USERLIST LDAPUSERSEARCH USERQUOTA IP NL NUMBER +%token DOMAIN USER USERLIST LDAPUSERSEARCH EXECUSERSEARCH USERQUOTA IP NL NUMBER %token PASS REDIRECT LOGDIR SUBST CHAR MINUTELY HOURLY DAILY WEEKLY DATE %token WITHIN OUTSIDE ELSE LOGFILE ANONYMOUS VERBOSE CONTINIOUS SPORADIC -%token LDAPCACHETIME EXECUSERLIST EXECCMD LDAPPROTOVER +%token USERCACHETIME EXECUSERLIST EXECCMD LDAPPROTOVER %token LDAPBINDDN LDAPBINDPASS %type WORD @@ -123,7 +123,7 @@ logdir: LOGDIR WORD { sgSetting("logdir",$2); } ; -ldapcachetime: LDAPCACHETIME NUMBER { sgSetting("ldapcachetime",$2); } +usercachetime: USERCACHETIME NUMBER { sgSetting("usercachetime",$2); } ; ldapprotover: LDAPPROTOVER NUMBER {sgSetting("ldapprotover",$2); } @@ -187,6 +187,7 @@ | USERLIST WORD { sgSourceUserList($2); } @YACCLINE@ | EXECUSERLIST EXECCMD { sgSourceExecUserList($2); } + | EXECUSERSEARCH EXECCMD { sgSourceExecUserSearch($2); } | USERQUOTA NUMBER NUMBER HOURLY { sgSourceUserQuota($2,$3,"3600");} | USERQUOTA NUMBER NUMBER DAILY { @@ -345,7 +346,7 @@ | ldapprotover | ldapbinddn | ldapbindpass - | ldapcachetime + | usercachetime | acl_block | rew_block | time_block @@ -689,6 +690,39 @@ #endif #if __STDC__ +void sgSourceExecUserSearch(char *program) +#else +void sgSourceExecUserSearch(program) + char *program; +#endif +{ + struct Source *sp; + sp = lastSource; + int i = 1; + char *tmp = strdup(program); + /* Create argv, with one extra slot for username */ + char **args = sgMalloc(sizeof(*args) * (i+2)); + args[0] = strtok(tmp, " \t"); + while((args[i] = strtok(NULL, "\t ")) != NULL) { + i++; + args = sgRealloc(args, sizeof(*args) * (i+2)); + } + + sp->queryprograms = (char***) sgRealloc(sp->queryprograms, + sizeof(char**) * (sp->queryprogramcount+1)); + sp->queryprograms[sp->queryprogramcount] = args; + sp->queryprogramcount++; + + /* create a userDb if it doesn't exist, since we'll need it later + * for caching */ + if(sp->userDb == NULL){ + sp->userDb = (struct sgDb *) sgCalloc(1,sizeof(struct sgDb)); + sp->userDb->type=SGDBTYPE_USERLIST; + sgDbInit(sp->userDb,NULL); + } +} + +#if __STDC__ void sgSourceExecUserList(char *cmd) #else void sgSourceExecUserList(cmd) @@ -950,12 +984,8 @@ if(*ident == '\0') founduser = 0; else { -#ifdef HAVE_LIBLDAP - if(sgFindUser(s, ident, &userquota)) { -#else rfc1738_unescape(ident); - if(defined(s->userDb, ident, (char **) &userquota) == 1){ -#endif + if(sgFindUser(s, ident, &userquota)) { founduser = 1; unblockeduser = 1; if(s->userquota.seconds != 0){ @@ -2461,7 +2491,6 @@ /* defined in the userDB? */ if(defined(src->userDb, ident, (char **) &userinfo) == 1) { -#ifdef HAVE_LIBLDAP /* LDAP user? */ if(!userinfo->ldapuser) { *rval = userinfo; @@ -2471,14 +2500,13 @@ /* from here on, we assume it is an LDAP user */ /* is this info valid? */ - interval = sgSettingGetValue("ldapcachetime"); + interval = sgSettingGetValue("usercachetime"); CacheTimeOut = atoi(interval != NULL ? interval : "0"); if((time(NULL) - userinfo->cachetime) <= CacheTimeOut) { if(userinfo->found) *rval = userinfo; return userinfo->found; /* yes */ } -#endif } else { userinfo = NULL; /* no record defined, must add our own*/ @@ -2486,6 +2514,43 @@ found = 0; /* assume not found */ + /* loop through all query programs and do a search */ + for(i = 0; i < src->queryprogramcount; i++) { + + found = sgDoQueryProgram(src->queryprograms[i], ident); + + /* cache every search in the user database */ + /* this should be safe, since squid only sends real idents + that have been authenticated (?) */ + + /* any record defined from above? */ + if(userinfo == NULL) { + /* no, must use our own memory */ + userinfo = &info; + info.status = 0; + info.time = 0; + info.consumed = 0; + info.last = 0; + info.ldapuser = 1; + info.found = found; + info.cachetime = time(NULL); + } + else { + /* yes, just update the found flag */ + userinfo->found = found; + userinfo->cachetime = time(NULL); + } + + sgDbUpdate(src->userDb, ident, (char *) userinfo, + sizeof(struct UserInfo)); + @NOLOG1@ sgLogError("Added program source: %s", ident); @NOLOG2@ + + if(found) { + *rval = userinfo; + break; + } + } + #ifdef HAVE_LIBLDAP /* loop through all LDAP URLs and do a search */ for(i = 0; i < src->ldapurlcount; i++) { @@ -2527,6 +2592,48 @@ return found; } +/* queries a program and returns 1 if found, 0 if not */ +#if __STDC__ +int sgDoQueryProgram(char **args, const char *username) +#else +int sgDoQueryProgram(args, username) + char **args; + const char *username; +#endif +{ + pid_t pid = fork(); + if (pid > 0) { + int rc; + int status; +again: + rc = wait(&status); + if (rc < 0 && errno == EINTR) + goto again; + if (rc != pid) { + sgLogError("%s: unexpected child process '%d'", rc); + goto again; + } + if (WIFEXITED(status)) { + return WEXITSTATUS(status) == 0; + } else if (WIFSIGNALED(status)) { + sgLogError("%s: query child killed by signal '%d'", WTERMSIG(status)); + return 0; + } + goto again; + } else if (pid == 0) { + int i; + for (i = 0; args[i] != NULL; i++); + args[i] = username; + args[i+1] = NULL; + execvp(args[0], args); + sgLogError("%s: unable to run query program '%s'", progname, args[0]); + return 1; + } else { + sgLogError("%s: unable to fork child process, out of memory?", progname); + return 0; + } +} + #ifdef HAVE_LIBLDAP #if __STDC__