/* $Header: /CVSROOT/squidauthdbm/squidauthdbm.c,v 1.1 2005/09/27 23:23:25 tino Exp $ * * A simple dbm driven Squid authenticator * * This source shall be independent of others. Therefor no tinolib. * * Copyright (C)2005 by Valentin Hilbig * * This program 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 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * $Log: squidauthdbm.c,v $ * Revision 1.1 2005/09/27 23:23:25 tino * first version * */ #include #include #include #include #include #include #include "squidauthdbm_version.h" static int debugging; #if 1 #include #define DP(X) do { if (debugging) dprintf X; } while (0) static void dprintf(const char *s, ...) { va_list list; fprintf(stderr, "["); va_start(list, s); vfprintf(stderr, s, list); va_end(list); fprintf(stderr, "]\n"); } #else #define DP(X) do { ; } while (0) #endif static void ex(const char *s, const char *arg) { int e; e = errno; fputs("error: ", stderr); fputs(s, stderr); if (arg) { fputs(": ", stderr); fputs(arg, stderr); } fputs(": ", stderr); fputs(strerror(e), stderr); fputs(": ", stderr); fputs(gdbm_strerror(gdbm_errno), stderr); fputs("\n", stderr); exit(-1); } static void * my_realloc(void *ptr, size_t len) { void *tmp; tmp = (ptr ? realloc(ptr, len) : malloc(len)); if (!tmp) ex("out of memory", NULL); return tmp; } static void * alloc(size_t len) { return my_realloc(NULL, len); } static void fatal_func(const char *s) { ex("gdbm fatal", s); } static GDBM_FILE db; static datum data; static char *dbmname; static void db_open(void) { if (db) return; DP(("db_open()")); if ((db=gdbm_open(dbmname, BUFSIZ, GDBM_WRCREAT, 0775, fatal_func))==0) ex("cannot open db", dbmname); } static void db_close(void) { if (db) { DP(("db_close")); gdbm_sync(db); gdbm_close(db); } db = 0; } static int db_del(char *s) { datum key; int ret; DP(("db_del(%s)", s)); key.dptr = s; key.dsize = strlen(s); db_open(); ret = gdbm_delete(db, key); DP(("db_del %d", ret)); return ret; } static const char * db_get(char *s) { datum key; DP(("db_get(%s)", s)); key.dptr = s; key.dsize = strlen(s); if (data.dptr) free(data.dptr); db_open(); data = gdbm_fetch(db, key); if (!data.dptr) { DP(("db_get notfound")); return 0; } data.dptr = my_realloc(data.dptr, data.dsize+1); data.dptr[data.dsize] = 0; DP(("db_get %s", data.dptr)); return data.dptr; } static int db_put(char *s, char *val) { datum key; int ret; DP(("db_put(%s,%s)", s, val)); key.dptr = s; key.dsize = strlen(s); if (data.dptr) free(data.dptr); data.dptr = val; data.dsize = strlen(val); db_open(); ret = gdbm_store(db, key, data, GDBM_REPLACE); data.dptr = 0; DP(("db_put %d", ret)); return ret; } static int pw_cmp(char *user, char *pw) { const char *ans; DP(("pw_cmp(%s,%s)", user, pw)); if (!pw) return -1; ans = db_get(user); if (!ans) return 2; DP(("pw_cmp %s", ans)); /* Default algorithm? */ switch (*ans++) { case '+': return !!strcmp(pw, ans); /* Support future other password store methods as well. */ } return 1; } /* In case we expect something weird in pw_cmp * (like encrypted PWs) */ static int pw_store(char *user, const char *pw) { size_t len; char *s; int ret; DP(("pw_store(%s,%s)", user, pw)); len = strlen(pw); s = alloc(len+2); *s = '+'; strcpy(s+1, pw); ret = db_put(user, s); free(s); DP(("pw_store %d", ret)); return ret; } static int do_cmd(char *line) { char *arg, *tmp; DP(("do_cmd(%s)", line)); arg = 0; for (tmp=line; *tmp; tmp++) if (*tmp==' ') { /* More than 1 arg, invalid */ if (arg) return 1; arg = tmp+1; *tmp = 0; } switch (*line) { default: return 1; case '+': line++; case '-': if (!arg) return db_del(line); return pw_store(line, arg); } } /* Yes, this program *is* stupid simple */ int main(int argc, char **argv) { char buf[1000]; int ok; data.dptr = 0; /* Create database name according to program invocation */ dbmname = alloc(strlen(argv[0])+5); strcpy(dbmname, argv[0]); strcat(dbmname, ".dbm"); if (argc>1 && !strcmp(argv[1], "debug")) { argv++; argc--; debugging = 1; DP(("debug on")); } if (argc>1) { /* Gather commandline into buffer * Why this way? * Command parsing is more robust this way. */ DP(("dbmname %s", dbmname)); buf[0] = 0; while (*++argv) { int i; i = strlen(buf); if (strlen(*argv)+i+2>=sizeof buf) ex("commandline too long", NULL); buf[i] = ' '; strcpy(buf+i+1, *argv); } return do_cmd(buf+1); } DP(("cmdloop")); /* The squid loop */ for (; db_close(), fgets(buf, sizeof buf-1, stdin); puts(ok ? "OK" : "ERR"), fflush(stdout)) { char *tmp, *arg1, *arg2; int spc; DP(("buf '%s'", buf)); ok = 0; arg1 = 0; arg2 = 0; spc = 0; for (tmp=buf;; tmp++) { switch (*tmp) { case ' ': if (!arg1) arg1 = tmp; arg2 = tmp; case '\r': case '\n': *tmp = 0; default: continue; case 0: break; } break; } DP(("buf='%s' arg1='%s' arg2='%s'", buf, arg1 ? arg1+1 : "(null)", arg2 ? arg2+1 : "(null)")); if (!arg2) continue; /* That's very magic. * * In case of user+pw both pointers are identical and thus * it does plain nothing (stays with separation). * * In case of a command it concatenates the first arg, * while it separates the second (which is the admin PW). * In case of only one arg (only admin PW) this stays them * both separated. */ *arg1++ = ' '; *arg2++ = 0; /* Do we have a command? */ if (buf[0]=='-' || buf[0]=='+') { if (!pw_cmp("-admin", arg2)) do_cmd(buf); continue; } /* 3 args are disallowed in case of user/pw checking */ if (arg1!=arg2) continue; switch (pw_cmp(buf, arg1)) { case 2: /* If user was not found and autolearning is enabled, * record the PW into the database. */ if (pw_cmp("-autolearn", "on") || pw_store(buf, arg1)) break; case 0: ok = 1; break; } } return 1; }