/* user.c - user handling functions for upsd Copyright (C) 2001 Russell Kroll 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 */ #include #include #include #include #include "common.h" #include "parseconf.h" #include "user.h" #include "user-data.h" #include "access.h" ulist_t *users = NULL; static ulist_t *curr_user; /* create a new user entry */ static void user_add(const char *un) { ulist_t *tmp, *last; if (!un) return; tmp = last = users; while (tmp) { last = tmp; if (!strcmp(tmp->username, un)) { fprintf(stderr, "Ignoring duplicate user %s\n", un); return; } tmp = tmp->next; } tmp = xmalloc(sizeof(ulist_t)); tmp->username = xstrdup(un); tmp->firstacl = NULL; tmp->password = NULL; tmp->firstcmd = NULL; tmp->firstaction = NULL; tmp->next = NULL; if (last) last->next = tmp; else users = tmp; /* remember who we're working on */ curr_user = tmp; } static acllist_t *addallow(acllist_t *base, const char *acl) { acllist_t *tmp, *last; if (!acl) return base; tmp = last = base; while (tmp) { last = tmp; tmp = tmp->next; } tmp = xmalloc(sizeof(acllist_t)); tmp->aclname = xstrdup(acl); tmp->next = NULL; if (last) { last->next = tmp; return base; } return tmp; } /* attach allowed hosts to user */ static void user_add_allow(const char *host) { if (!curr_user) { upslogx(LOG_WARNING, "Ignoring allowfrom definition outside " "user section"); return; } curr_user->firstacl = addallow(curr_user->firstacl, host); } /* set password */ static void user_password(const char *pw) { if (!curr_user) { upslogx(LOG_WARNING, "Ignoring password definition outside " "user section"); return; } if (!pw) return; if (curr_user->password) { fprintf(stderr, "Ignoring duplicate password for %s\n", curr_user->username); return; } curr_user->password = xstrdup(pw); } /* attach allowed instcmds to user */ static void user_add_instcmd(const char *cmd) { instcmdlist_t *tmp, *last; if (!curr_user) { upslogx(LOG_WARNING, "Ignoring instcmd definition outside " "user section"); return; } if (!cmd) return; tmp = curr_user->firstcmd; last = NULL; while (tmp) { last = tmp; /* ignore duplicates */ if (!strcasecmp(tmp->cmd, cmd)) return; tmp = tmp->next; } tmp = xmalloc(sizeof(instcmdlist_t)); tmp->cmd = xstrdup(cmd); tmp->next = NULL; if (last) last->next = tmp; else curr_user->firstcmd = tmp; } static actionlist_t *addaction(actionlist_t *base, const char *action) { actionlist_t *tmp, *last; if (!action) return base; tmp = last = base; while (tmp) { last = tmp; tmp = tmp->next; } tmp = xmalloc(sizeof(actionlist_t)); tmp->action = xstrdup(action); tmp->next = NULL; if (last) { last->next = tmp; return base; } return tmp; } /* attach allowed actions to user */ static void user_add_action(const char *act) { if (!curr_user) { upslogx(LOG_WARNING, "Ignoring action definition outside " "user section"); return; } curr_user->firstaction = addaction(curr_user->firstaction, act); } static void flushacl(acllist_t *first) { acllist_t *ptr, *next; ptr = first; while (ptr) { next = ptr->next; free(ptr->aclname); free(ptr); ptr = next; } } static void flushcmd(instcmdlist_t *first) { instcmdlist_t *ptr, *next; ptr = first; while (ptr) { next = ptr->next; free(ptr->cmd); free(ptr); ptr = next; } } static void flushaction(actionlist_t *first) { actionlist_t *ptr, *next; ptr = first; while (ptr) { next = ptr->next; free(ptr->action); free(ptr); ptr = next; } } /* flush all user attributes - used during reload */ void user_flush(void) { ulist_t *ptr, *next; ptr = users; while (ptr) { next = ptr->next; if (ptr->firstacl) flushacl(ptr->firstacl); if (ptr->firstcmd) flushcmd(ptr->firstcmd); if (ptr->firstaction) flushaction(ptr->firstaction); free(ptr->username); free(ptr->password); free(ptr); ptr = next; } users = NULL; } #ifndef HAVE_IPV6 static int user_matchacl(ulist_t *user, const struct sockaddr_in *addr) #else static int user_matchacl(ulist_t *user, const struct sockaddr_storage *addr) #endif { acllist_t *tmp; tmp = user->firstacl; /* no acls means no access (fail-safe) */ if (!tmp) return 0; /* good */ while (tmp) { if (acl_check(tmp->aclname, addr) == 1) return 1; /* good */ tmp = tmp->next; } return 0; /* fail */ } static int user_matchinstcmd(ulist_t *user, const char * cmd) { instcmdlist_t *tmp = user->firstcmd; /* no commands means no access (fail-safe) */ if (!tmp) return 0; /* fail */ while (tmp) { if ((!strcasecmp(tmp->cmd, cmd)) || (!strcasecmp(tmp->cmd, "all"))) return 1; /* good */ tmp = tmp->next; } return 0; /* fail */ } #ifndef HAVE_IPV6 int user_checkinstcmd(const struct sockaddr_in *addr, #else int user_checkinstcmd(const struct sockaddr_storage *addr, #endif const char *un, const char *pw, const char *cmd) { ulist_t *tmp = users; if ((!un) || (!pw) || (!cmd)) return 0; /* failed */ while (tmp) { /* let's be paranoid before we call strcmp */ if ((!tmp->username) || (!tmp->password)) { tmp = tmp->next; continue; } if (!strcmp(tmp->username, un)) { if (!strcmp(tmp->password, pw)) { if (!user_matchacl(tmp, addr)) return 0; /* fail */ if (!user_matchinstcmd(tmp, cmd)) return 0; /* fail */ /* passed all checks */ return 1; /* good */ } /* password mismatch */ return 0; /* fail */ } tmp = tmp->next; } /* username not found */ return 0; /* fail */ } static int user_matchaction(ulist_t *user, const char *action) { actionlist_t *tmp = user->firstaction; /* no actions means no access (fail-safe) */ if (!tmp) return 0; /* fail */ while (tmp) { if (!strcasecmp(tmp->action, action)) return 1; /* good */ tmp = tmp->next; } return 0; /* fail */ } #ifndef HAVE_IPV6 int user_checkaction(const struct sockaddr_in *addr, #else int user_checkaction(const struct sockaddr_storage *addr, #endif const char *un, const char *pw, const char *action) { ulist_t *tmp = users; if ((!un) || (!pw) || (!action)) return 0; /* failed */ while (tmp) { /* let's be paranoid before we call strcmp */ if ((!tmp->username) || (!tmp->password)) { tmp = tmp->next; continue; } if (!strcmp(tmp->username, un)) { if (!strcmp(tmp->password, pw)) { if (!user_matchacl(tmp, addr)) { upsdebugx(2, "user_matchacl: failed"); return 0; /* fail */ } if (!user_matchaction(tmp, action)) { upsdebugx(2, "user_matchaction: failed"); return 0; /* fail */ } /* passed all checks */ return 1; /* good */ } /* password mismatch */ upsdebugx(2, "user_checkaction: password mismatch"); return 0; /* fail */ } tmp = tmp->next; } /* username not found */ return 0; /* fail */ } /* handle "upsmon master" and "upsmon slave" for nicer configurations */ static void set_upsmon_type(char *type) { /* master: login, master, fsd */ if (!strcasecmp(type, "master")) { user_add_action("login"); user_add_action("master"); user_add_action("fsd"); return; } /* slave: just login */ if (!strcasecmp(type, "slave")) { user_add_action("login"); return; } upslogx(LOG_WARNING, "Unknown upsmon type %s", type); } /* actually do something with the variable + value pairs */ static void parse_var(char *var, char *val) { if (!strcasecmp(var, "password")) { user_password(val); return; } if (!strcasecmp(var, "instcmds")) { user_add_instcmd(val); return; } if (!strcasecmp(var, "actions")) { user_add_action(val); return; } if (!strcasecmp(var, "allowfrom")) { user_add_allow(val); return; } /* someone did 'upsmon = type' - allow it anyway */ if (!strcasecmp(var, "upsmon")) { set_upsmon_type(val); return; } upslogx(LOG_NOTICE, "Unrecognized user setting %s", var); } /* parse first var+val pair, then flip through remaining vals */ static void parse_rest(char *var, char *fval, char **arg, int next, int left) { int i; /* no globals supported yet, so there's no sense in continuing */ if (!curr_user) return; parse_var(var, fval); if (left == 0) return; for (i = 0; i < left; i++) parse_var(var, arg[next + i]); } static void user_parse_arg(int numargs, char **arg) { char *ep; if ((numargs == 0) || (!arg)) return; /* ignore old file format */ if (!strcasecmp(arg[0], "user")) return; /* handle 'foo=bar' (compressed form) */ ep = strchr(arg[0], '='); if (ep) { *ep = '\0'; /* parse first var/val, plus subsequent values (if any) */ /* 0 1 2 ... */ /* foo=bar ... */ parse_rest(arg[0], ep+1, arg, 1, numargs - 1); return; } /* look for section headers - [username] */ if ((arg[0][0] == '[') && (arg[0][strlen(arg[0])-1] == ']')) { arg[0][strlen(arg[0])-1] = '\0'; user_add(&arg[0][1]); return; } if (numargs < 2) return; if (!strcasecmp(arg[0], "upsmon")) set_upsmon_type(arg[1]); /* everything after here needs arg[1] and arg[2] */ if (numargs < 3) return; /* handle 'foo = bar' (split form) */ if (!strcmp(arg[1], "=")) { /* 0 1 2 3 4 ... */ /* foo = bar ... */ /* parse first var/val, plus subsequent values (if any) */ parse_rest(arg[0], arg[2], arg, 3, numargs - 3); return; } /* ... unhandled ... */ } /* called for fatal errors in parseconf like malloc failures */ static void upsd_user_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf(upsd.users): %s", errmsg); } void user_load(void) { char fn[SMALLBUF]; PCONF_CTX_t ctx; curr_user = NULL; snprintf(fn, sizeof(fn), "%s/upsd.users", confpath()); check_perms(fn); pconf_init(&ctx, upsd_user_err); if (!pconf_file_begin(&ctx, fn)) { pconf_finish(&ctx); upslogx(LOG_WARNING, "%s", ctx.errmsg); return; } while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", fn, ctx.linenum, ctx.errmsg); continue; } user_parse_arg(ctx.numargs, ctx.arglist); } pconf_finish(&ctx); }