/
niceSOFT
/
inetutils
Обзор
Документация
Войти
/
niceSOFT
/
inetutils
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
talkd/acl.c
444 строки
9 KB
Simon Josefsson
maint: Run 'make update-copyright'
20 янв 2026, 11:50
Не верифицирован
20 янв 2026, 11:50
1ce61cd
Код
Авторство
О чём код?
/* Copyright (C) 2001-2026 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils 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 3 of the License, or (at your option) any later version. GNU Inetutils 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, see `http://www.gnu.org/licenses/'. */ #include <config.h> #include <intalkd.h> #include <pwd.h> #include <regex.h> #include <ctype.h> #include "argcv.h" typedef struct netdef netdef_t; struct netdef { netdef_t *next; in_addr_t ipaddr; in_addr_t netmask; }; typedef struct acl acl_t; struct acl { acl_t *next; regex_t re; netdef_t *netlist; int system; int action; }; acl_t *acl_head, *acl_tail; #define DOTTED_QUAD_LEN 16 static int read_address (char **line_ptr, char *ptr) { char *startp = *line_ptr; char *endp; int dotcount = 0; for (endp = startp; *endp; endp++, ptr++) if (!(isdigit (*endp) || *endp == '.')) break; else if (endp < startp + DOTTED_QUAD_LEN) { if (*endp == '.') dotcount++; *ptr = *endp; } else break; *line_ptr = endp; *ptr = 0; return dotcount; } static netdef_t * netdef_parse (char *str) { in_addr_t ipaddr, netmask; netdef_t *netdef; char ipbuf[DOTTED_QUAD_LEN + 1]; if (strcmp (str, "any") == 0) { ipaddr = 0; netmask = 0; } else { read_address (&str, ipbuf); ipaddr = inet_addr (ipbuf); if (ipaddr == INADDR_NONE) return NULL; if (*str == 0) netmask = 0xfffffffful; else if (*str != '/') return NULL; else { str++; if (read_address (&str, ipbuf) == 0) { /* netmask length */ unsigned int len = strtoul (ipbuf, NULL, 0); if (len > 32) return NULL; netmask = 0xfffffffful >> (32 - len); netmask <<= (32 - len); /*FIXME: hostorder? */ } else netmask = inet_network (ipbuf); netmask = htonl (netmask); } } netdef = malloc (sizeof *netdef); if (!netdef) { syslog (LOG_ERR, "Out of memory"); exit (EXIT_FAILURE); } netdef->next = NULL; netdef->ipaddr = ipaddr; netdef->netmask = netmask; return netdef; } void read_acl (char *config_file, int system) { FILE *fp; int line; char buf[128]; char *ptr; if (!config_file || !config_file[0]) return; fp = fopen (config_file, "r"); if (!fp) { if (system > 0) { /* A missing, yet specified, site-wide ACL is a serious error. * Abort execution whenever this happens. */ syslog (LOG_ERR, "Cannot open config file %s: %m", config_file); exit (EXIT_FAILURE); } return; /* User setting may fail to exist. Just ignore. */ } if (system < 0) { /* Alarmed state, violating file access policy. * Insert a single, general denial equivalent to * a rule reading `deny .* any'. */ const char any_user[] = "^.*$"; char any_host[] = "any"; acl_t *acl; regex_t re; netdef_t *cur; fclose (fp); acl = malloc (sizeof *acl); if (!acl) { syslog (LOG_ERR, "Out of memory"); exit (EXIT_FAILURE); } if (regcomp (&re, any_user, 0) != 0) { syslog (LOG_ERR, "Bad regexp '%s'.", any_user); exit (EXIT_FAILURE); } cur = netdef_parse (any_host); if (!cur) { syslog (LOG_ERR, "Cannot parse netdef '%s'.", any_host); exit (EXIT_FAILURE); } acl->next = NULL; acl->action = ACL_DENY; acl->system = 0; acl->re = re; acl->netlist = cur; if (!acl_tail) acl_head = acl; else acl_tail->next = acl; acl_tail = acl; return; } line = 0; while ((ptr = fgets (buf, sizeof buf, fp))) { int len, i; int argc; char **argv; int action; regex_t re; netdef_t *head, *tail; acl_t *acl; line++; len = strlen (ptr); if (len > 0 && ptr[len - 1] == '\n') ptr[--len] = 0; while (*ptr && isspace (*ptr)) ptr++; if (!*ptr || *ptr == '#') continue; argcv_get (ptr, "", &argc, &argv); if (argc < 2) { syslog (LOG_ERR, "%s:%d: too few fields", config_file, line); argcv_free (argc, argv); continue; } if (strcmp (argv[0], "allow") == 0) action = ACL_ALLOW; else if (strcmp (argv[0], "deny") == 0) action = ACL_DENY; else { syslog (LOG_WARNING, "%s:%d: unknown keyword", config_file, line); argcv_free (argc, argv); continue; } if (regcomp (&re, argv[1], 0) != 0) { syslog (LOG_WARNING, "%s:%d: bad regexp", config_file, line); argcv_free (argc, argv); continue; } head = tail = NULL; for (i = 2; i < argc; i++) { netdef_t *cur = netdef_parse (argv[i]); if (!cur) { syslog (LOG_ERR, "%s:%d: can't parse netdef: %s", config_file, line, argv[i]); continue; } if (!tail) head = cur; else tail->next = cur; tail = cur; } argcv_free (argc, argv); acl = malloc (sizeof *acl); if (!acl) { syslog (LOG_ERR, "Out of memory"); exit (EXIT_FAILURE); } acl->next = NULL; acl->action = action; acl->system = system; acl->netlist = head; acl->re = re; if (!acl_tail) acl_head = acl; else acl_tail->next = acl; acl_tail = acl; } fclose (fp); } static acl_t * open_users_acl (char *name) { int level = 0; /* Private file, not mandatory. */ int rc; char *filename; struct passwd *pw; struct stat st; acl_t *mark; pw = getpwnam (name); if (!pw) return NULL; filename = malloc (strlen (pw->pw_dir) + sizeof (USER_ACL_NAME) + 2 /* Null and separator. */ ); if (!filename) { syslog (LOG_ERR, "Out of memory"); return NULL; } sprintf (filename, "%s/%s", pw->pw_dir, USER_ACL_NAME); /* The location must be a file, and must be owned by the * indicated user and his corresponding group. No write * access by group or world. Record a syslog warning, * should either of these not be true. */ rc = stat (filename, &st); if (rc < 0) return NULL; if (!S_ISREG (st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != pw->pw_gid || st.st_mode & S_IWGRP || st.st_mode & S_IWOTH) { if (logging || debug) syslog (LOG_WARNING, "Discarding '%s': insecure access.", filename); level = -1; /* Enforce a deny rule. */ } mark = acl_tail; read_acl (filename, level); free (filename); return mark; } static void netdef_free (netdef_t *netdef) { netdef_t *next; while (netdef) { next = netdef->next; free (netdef); netdef = next; } } static void acl_free (acl_t *acl) { acl_t *next; while (acl) { next = acl->next; regfree (&acl->re); netdef_free (acl->netlist); free (acl); acl = next; } } static void discard_acl (acl_t *mark) { if (mark) { acl_free (mark->next); acl_tail = mark; acl_tail->next = NULL; } else acl_head = acl_tail = NULL; } int acl_match (CTL_MSG *msg, struct sockaddr_in *sa_in) { acl_t *acl, *mark; in_addr_t ip; int system_action = ACL_ALLOW, user_action = ACL_ALLOW; int found_user_acl = 0; if (strict_policy) system_action = ACL_DENY; mark = open_users_acl (msg->r_name); ip = sa_in->sin_addr.s_addr; if (mark && (mark != acl_tail)) found_user_acl = 1; for (acl = acl_head; acl; acl = acl->next) { netdef_t *net; for (net = acl->netlist; net; net = net->next) { /* Help the administrator and his users * to simplify net list syntax: * * mask the address `net->ipaddr' with * `net->netmask' for less computations * within the ACL specification. */ if ((net->ipaddr & net->netmask) == (ip & net->netmask)) { /* * Site-wide ACLs concern user's name on this machine, * whereas user's rules concern the incoming client name. */ if (acl->system && regexec (&acl->re, msg->r_name, 0, NULL, 0) == 0) system_action = acl->action; else if (regexec (&acl->re, msg->l_name, 0, NULL, 0) == 0) user_action = acl->action; } } } discard_acl (mark); if (system_action == ACL_ALLOW) return user_action; if (strict_policy) return ACL_DENY; /* Equal to `system_action'. */ /* At this point it is known that last activated site-wide * ACL rule has set SYSTEM_ACTION to ACL_DENY. Do we * always want it to be overridable? */ /* Override ACL_DENY only if there was a user specific file * ~/.talkrc containing some active rules at all. In other * words, a site-policy claiming `deny' will need an act of * will by the user in order that it be overridden. */ return found_user_acl ? user_action : ACL_DENY; }