|
Packit |
ddac9e |
/* authd, a lightweight IPv6/IPv4 inetd RFC 1413 ident protocol daemon
|
|
Packit |
ddac9e |
* Copyright 2004 by Red Hat, Inc.
|
|
Packit |
ddac9e |
*
|
|
Packit |
ddac9e |
* THIS PROGRAM IS RELEASED UNDER THE GPL WITH THE ADDITIONAL EXEMPTION
|
|
Packit |
ddac9e |
* THAT COMPILING, LINKING, AND/OR USING OPENSSL IS ALLOWED.
|
|
Packit |
ddac9e |
*
|
|
Packit |
ddac9e |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
ddac9e |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
ddac9e |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
ddac9e |
* (at your option) any later version.
|
|
Packit |
ddac9e |
*
|
|
Packit |
ddac9e |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
ddac9e |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
ddac9e |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
ddac9e |
* GNU General Public License for more details.
|
|
Packit |
ddac9e |
*
|
|
Packit |
ddac9e |
* You should have received a copy of the GNU General Public License
|
|
Packit |
ddac9e |
* along with this program; if not, write to the Free Software
|
|
Packit |
ddac9e |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
ddac9e |
* 02110-1301, USA.
|
|
Packit |
ddac9e |
*
|
|
Packit |
ddac9e |
* Initial Author: Adrian Havill <havill@redhat.com>
|
|
Packit |
ddac9e |
*/
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static const char RCSID[] = "$Revision: 1.18 $ $Date: 2004/07/28 16:04:05 $";
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#include "config.h"
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#define _GNU_SOURCE
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#include <assert.h>
|
|
Packit |
ddac9e |
#include <ctype.h>
|
|
Packit |
ddac9e |
#include <errno.h>
|
|
Packit |
ddac9e |
#include <limits.h>
|
|
Packit |
ddac9e |
#include <locale.h>
|
|
Packit |
ddac9e |
#include <signal.h>
|
|
Packit |
ddac9e |
#include <stdarg.h>
|
|
Packit |
ddac9e |
#include <stdbool.h>
|
|
Packit |
ddac9e |
#include <stdio.h>
|
|
Packit |
ddac9e |
#include <stdlib.h>
|
|
Packit |
ddac9e |
#include <string.h>
|
|
Packit |
ddac9e |
#include <time.h>
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#include <arpa/inet.h>
|
|
Packit |
ddac9e |
#include <netinet/in.h>
|
|
Packit |
ddac9e |
#include <sys/utsname.h>
|
|
Packit |
ddac9e |
#include <sys/socket.h>
|
|
Packit |
ddac9e |
#include <sys/stat.h>
|
|
Packit |
ddac9e |
#include <sys/types.h>
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#include <getopt.h>
|
|
Packit |
ddac9e |
#include <langinfo.h>
|
|
Packit |
ddac9e |
#include <libintl.h>
|
|
Packit |
ddac9e |
#include <netdb.h>
|
|
Packit |
ddac9e |
#include <pwd.h>
|
|
Packit |
ddac9e |
#include <syslog.h>
|
|
Packit |
ddac9e |
#include <unistd.h>
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#include <openssl/bio.h>
|
|
Packit |
ddac9e |
#include <openssl/buffer.h>
|
|
Packit |
ddac9e |
#include <openssl/evp.h>
|
|
Packit |
ddac9e |
#include <openssl/err.h>
|
|
Packit |
ddac9e |
#include <openssl/rand.h>
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#if !defined NDEBUG
|
|
Packit |
ddac9e |
#include <mcheck.h>
|
|
Packit |
ddac9e |
#endif
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#define _(ascii_msgid) gettext(ascii_msgid)
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static struct {
|
|
Packit |
ddac9e |
int log_mask;
|
|
Packit |
ddac9e |
int abrupt, help, hybrid, resolve, verbose, xerror; // longopt bool is pint
|
|
Packit |
ddac9e |
bool debug, error, log, number, other, Version;
|
|
Packit |
ddac9e |
char *codeset, *ident, *lang, *mapped, *os, *passwd, *username;
|
|
Packit |
ddac9e |
char *Encrypt, *Noident;
|
|
Packit |
ddac9e |
unsigned timeout, fn; unsigned long long multiquery;
|
|
Packit |
ddac9e |
} opt;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void log_printf(int level, const char *s, ...) {
|
|
Packit |
ddac9e |
if (opt.debug || level != LOG_DEBUG) {
|
|
Packit |
ddac9e |
va_list ap;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (s == NULL || *s == '\0') {
|
|
Packit |
ddac9e |
s = strerror(errno);
|
|
Packit |
ddac9e |
assert(strchr(s, '%') == NULL);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (opt.log) {
|
|
Packit |
ddac9e |
va_start(ap, s);
|
|
Packit |
ddac9e |
vsyslog(level, s, ap);
|
|
Packit |
ddac9e |
va_end(ap);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
va_start(ap, s);
|
|
Packit |
ddac9e |
if (vfprintf(level == LOG_INFO ? stdout : stderr, s, ap) < 0) {
|
|
Packit |
ddac9e |
perror(program_invocation_name);
|
|
Packit |
ddac9e |
level = INT_MIN;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
va_end(ap);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (level <= LOG_ERR) exit(EXIT_FAILURE);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
#define debug(...) log_printf(LOG_DEBUG, __VA_ARGS__)
|
|
Packit |
ddac9e |
#define show(...) log_printf(LOG_INFO, __VA_ARGS__)
|
|
Packit |
ddac9e |
#define log_notice(...) log_printf(LOG_NOTICE, __VA_ARGS__)
|
|
Packit |
ddac9e |
#define log_warning(...) log_printf(LOG_WARNING, __VA_ARGS__)
|
|
Packit |
ddac9e |
#define handle_error(...) log_printf(LOG_ERR, __VA_ARGS__)
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#if !defined NDEBUG
|
|
Packit |
ddac9e |
static const void *const LAST_VA_ARG = "FIXME"; // debugging sentinel
|
|
Packit |
ddac9e |
#endif
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *vstrdup(const char *s, ...) {
|
|
Packit |
ddac9e |
va_list ap; char *dup;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(s != LAST_VA_ARG);
|
|
Packit |
ddac9e |
va_start(ap, s);
|
|
Packit |
ddac9e |
while (s == NULL || *s == '\0') {
|
|
Packit |
ddac9e |
s = va_arg(ap, const char *);
|
|
Packit |
ddac9e |
assert(s != LAST_VA_ARG);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
va_end(ap);
|
|
Packit |
ddac9e |
if ((dup = strdup(s)) == NULL) handle_error(NULL);
|
|
Packit |
ddac9e |
return dup;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
#if !defined NDEBUG
|
|
Packit |
ddac9e |
#define vstrdup(s, ...) vstrdup((s) ,##__VA_ARGS__ ,LAST_VA_ARG)
|
|
Packit |
ddac9e |
#endif
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void show_help(void) {
|
|
Packit |
ddac9e |
show(_("%s [options]... [request]...\n"), program_invocation_short_name);
|
|
Packit |
ddac9e |
show(_(" -d\t\t\tOutput debug messages\n"));
|
|
Packit |
ddac9e |
show(_(" -E[cipher]\t\tEncrypt username using [%s]\n"), DFL_CIPHER);
|
|
Packit |
ddac9e |
show(_(" -e\t\t\tSend UNKNOWN-ERROR for common error messages\n"));
|
|
Packit |
ddac9e |
show(_(" -l[mask]\t\tLog to syslog, [mask] for priority levels\n"));
|
|
Packit |
ddac9e |
show(_(" -m[reps]\t\tAllow at most [unlimited] requests/connection\n"));
|
|
Packit |
ddac9e |
show(_(" -N[basename]\t\tHide users with ~/[%s] file\n"), DFL_NO_IDENT);
|
|
Packit |
ddac9e |
show(_(" -n\t\t\tSend uid number instead of username\n"));
|
|
Packit |
ddac9e |
show(_(" -o\t\t\tSend OTHER instead of the operating system\n"));
|
|
Packit |
ddac9e |
show(_(" -t[secs]\t\tTimeout request after [%u] seconds\n"), DFL_T_O);
|
|
Packit |
ddac9e |
show(_(" -V\t\t\tPrint program and version information\n"));
|
|
Packit |
ddac9e |
show(_(" --abrupt\t\tDisconnect without displaying error msgs\n"));
|
|
Packit |
ddac9e |
show(_(" --codeset=rfc1340\tSend [charset] if reply is not ASCII\n"));
|
|
Packit |
ddac9e |
show(_(" --fn[=fields]\t\tUse [all] gecos fields instead of username\n"));
|
|
Packit |
ddac9e |
show(_(" --hybrid\t\tUse hybrid notation (ex. ::127.0.0.1) for IPv6\n"));
|
|
Packit |
ddac9e |
show(_(" --ident[=basename]\tHide users with no ~/[%s]\n"), DFL_IDENT);
|
|
Packit |
ddac9e |
show(_(" --lang=lc\t\tLocalize messages, charsets, & time to [locale]\n"));
|
|
Packit |
ddac9e |
show(_(" --mapped=ipv6\t\tMap addresses with same top 96 bits to IPv4\n"));
|
|
Packit |
ddac9e |
show(_(" --os[=rfc1340]\t\tUse [uname], not %s, as OS name\n"), DFL_OS);
|
|
Packit |
ddac9e |
show(_(" --passwd=pathname\tUse line 1 of [%s] for crypto\n"), DFL_PASSWD);
|
|
Packit |
ddac9e |
show(_(" --resolve\t\tUse host and service names if available\n"));
|
|
Packit |
ddac9e |
show(_(" --username[=login]\tUse [%s] as username\n"), DFL_USERNAME);
|
|
Packit |
ddac9e |
show(_(" --verbose\t\tAdd real uid, addresses/ports, & timestamps\n"));
|
|
Packit |
ddac9e |
show(_(" --xerror\t\tSend more helpful error messages for rare errors\n"));
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void show_version(void) {
|
|
Packit |
ddac9e |
show("%s-%s:\n\t%s\n\t%s\n", PACKAGE, VERSION, RCSID, CONTACT);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static bool is_print_ascii(const char *s) {
|
|
Packit |
ddac9e |
assert(s != NULL);
|
|
Packit |
ddac9e |
while (*s != '\0')
|
|
Packit |
ddac9e |
if (!isascii(*s) || iscntrl(*s)) return false;
|
|
Packit |
ddac9e |
else s++;
|
|
Packit |
ddac9e |
return true;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
#define is_in_range(lo,x,hi) ((lo) <= (x) && (x) <= (hi))
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static bool is_rfc1413_token(const char *s) {
|
|
Packit |
ddac9e |
assert(s != NULL);
|
|
Packit |
ddac9e |
if (!is_print_ascii(s) || strchr(s, ':') != NULL) return false;
|
|
Packit |
ddac9e |
return is_in_range((size_t) 1, strlen(s), (size_t) 64);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static bool is_bad_strto(const char *s, const char *endptr) {
|
|
Packit |
ddac9e |
if (errno == ERANGE || errno == EINVAL) return true;
|
|
Packit |
ddac9e |
return endptr == s || (*endptr != '\0' && !isspace(*endptr));
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static const int HEX_DIG = 2; // two digits in 8-bit base 16 octet
|
|
Packit |
ddac9e |
static const size_t HEX_LEN_MAX = 32; // strlen() of /proc/net/tcp6 address
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *created_addr_hex(const unsigned char *addr, size_t addr_size) {
|
|
Packit |
ddac9e |
char *addr_hex;
|
|
Packit |
ddac9e |
size_t addr_hex_size = addr_size * HEX_DIG + sizeof '\0';
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if ((addr_hex = calloc(addr_hex_size, sizeof(char))) != NULL) {
|
|
Packit |
ddac9e |
size_t z = addr_size; char *p = addr_hex;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
while (z != 0) {
|
|
Packit |
ddac9e |
int n = snprintf(p, addr_hex_size, "%.*hhX", HEX_DIG, addr[--z]);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(n >= 0 && (size_t) n < addr_hex_size);
|
|
Packit |
ddac9e |
addr_hex_size -= (size_t) n; p += (size_t) n;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
return addr_hex;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *created_pton_hex(const char *prefix) {
|
|
Packit |
ddac9e |
struct in6_addr addr; const size_t SIZE = sizeof(addr.s6_addr);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(prefix != NULL);
|
|
Packit |
ddac9e |
if (inet_pton(AF_INET6, prefix, &addr) <= 0) return NULL;
|
|
Packit |
ddac9e |
return created_addr_hex((const void *) addr.s6_addr, SIZE);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void create_opt(int argc, char *argv[]) {
|
|
Packit |
ddac9e |
enum { PRE_FIRST_LONGOPT = UCHAR_MAX, // no short opt value overlap
|
|
Packit |
ddac9e |
CODESET_LONGOPT, IDENT_LONGOPT, FN_LONGOPT, LANG_LONGOPT,
|
|
Packit |
ddac9e |
MAPPED_LONGOPT, OS_LONGOPT, PASSWD_LONGOPT, USERNAME_LONGOPT
|
|
Packit |
ddac9e |
};
|
|
Packit |
ddac9e |
int c, i;
|
|
Packit |
ddac9e |
const char *const IPV4_HEX_0 = "00000000";
|
|
Packit |
ddac9e |
const char *const S_FMT = _("%s: invalid argument to --%s: %s\n");
|
|
Packit |
ddac9e |
const char *const C_FMT = _("%s: invalid argument to -%c: %s\n");
|
|
Packit |
ddac9e |
const char *const SHORT_OPTS = "dE::ehl::m::N::not::V"; // pidentd compat
|
|
Packit |
ddac9e |
const struct option LONG_OPTS[] = {
|
|
Packit |
ddac9e |
{ "abrupt", no_argument, &opt.abrupt, true },
|
|
Packit |
ddac9e |
{ "codeset", required_argument, NULL, CODESET_LONGOPT },
|
|
Packit |
ddac9e |
{ "fn", optional_argument, NULL, FN_LONGOPT },
|
|
Packit |
ddac9e |
{ "help", no_argument, NULL, 'h' },
|
|
Packit |
ddac9e |
{ "hybrid", no_argument, &opt.hybrid, true },
|
|
Packit |
ddac9e |
{ "ident", optional_argument, NULL, IDENT_LONGOPT },
|
|
Packit |
ddac9e |
{ "lang", required_argument, NULL, LANG_LONGOPT },
|
|
Packit |
ddac9e |
{ "mapped", required_argument, NULL, MAPPED_LONGOPT },
|
|
Packit |
ddac9e |
{ "os", optional_argument, NULL, OS_LONGOPT },
|
|
Packit |
ddac9e |
{ "passwd", required_argument, NULL, PASSWD_LONGOPT },
|
|
Packit |
ddac9e |
{ "resolve", no_argument, &opt.resolve, true },
|
|
Packit |
ddac9e |
{ "usage", no_argument, NULL, 'h' },
|
|
Packit |
ddac9e |
{ "username", optional_argument, NULL, USERNAME_LONGOPT },
|
|
Packit |
ddac9e |
{ "verbose", no_argument, &opt.verbose, true },
|
|
Packit |
ddac9e |
{ "xerror", no_argument, &opt.xerror, true },
|
|
Packit |
ddac9e |
{ 0, 0, 0, 0 }
|
|
Packit |
ddac9e |
};
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(argc > 0 && argv != NULL);
|
|
Packit |
ddac9e |
memset(&opt, 0, sizeof(opt));
|
|
Packit |
ddac9e |
opt.passwd = vstrdup(DFL_PASSWD);
|
|
Packit |
ddac9e |
if ((opt.mapped = calloc(HEX_LEN_MAX + sizeof '\0', sizeof(char))) == NULL)
|
|
Packit |
ddac9e |
handle_error(NULL);
|
|
Packit |
ddac9e |
memset(opt.mapped, '0', HEX_LEN_MAX);
|
|
Packit |
ddac9e |
opt.multiquery = 1;
|
|
Packit |
ddac9e |
opt.timeout = UINT_MAX;
|
|
Packit |
ddac9e |
while ((c = getopt_long(argc, argv, SHORT_OPTS, LONG_OPTS, &i)) != -1) {
|
|
Packit |
ddac9e |
switch (c) {
|
|
Packit |
ddac9e |
struct utsname os;
|
|
Packit |
ddac9e |
unsigned long lu; char *endptr, *lc;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
case 'd': opt.debug = true; break;
|
|
Packit |
ddac9e |
case 'E':
|
|
Packit |
ddac9e |
free(opt.Encrypt); opt.Encrypt = vstrdup(optarg, DFL_CIPHER);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case 'e': opt.error = true; break;
|
|
Packit |
ddac9e |
case 'h': opt.help = true; break;
|
|
Packit |
ddac9e |
case 'l':
|
|
Packit |
ddac9e |
if (optarg != NULL) {
|
|
Packit |
ddac9e |
lu = strtoul(optarg, &endptr, 0);
|
|
Packit |
ddac9e |
if (lu > UINT_MAX || is_bad_strto(optarg, endptr))
|
|
Packit |
ddac9e |
handle_error(C_FMT, *argv, c, optarg);
|
|
Packit |
ddac9e |
else opt.log_mask = (int) lu;
|
|
Packit |
ddac9e |
setlogmask(opt.log_mask);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
opt.log = true;
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case 'm':
|
|
Packit |
ddac9e |
if (optarg != NULL) {
|
|
Packit |
ddac9e |
opt.multiquery = strtoull(optarg, &endptr, 10);
|
|
Packit |
ddac9e |
if (is_bad_strto(optarg, endptr))
|
|
Packit |
ddac9e |
handle_error(C_FMT, *argv, c, optarg);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else opt.multiquery = ULLONG_MAX;
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case 'n': opt.number = true; break;
|
|
Packit |
ddac9e |
case 'N':
|
|
Packit |
ddac9e |
free(opt.Noident); opt.Noident = vstrdup(optarg, DFL_NO_IDENT);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case 'o': opt.other = true; break;
|
|
Packit |
ddac9e |
case 't':
|
|
Packit |
ddac9e |
lu = optarg == NULL ? DFL_T_O : strtoul(optarg, &endptr, 10);
|
|
Packit |
ddac9e |
if (lu > UINT_MAX || is_bad_strto(optarg, endptr))
|
|
Packit |
ddac9e |
handle_error(C_FMT, *argv, c, optarg);
|
|
Packit |
ddac9e |
else if (lu < 30) {
|
|
Packit |
ddac9e |
log_notice(_("Timeout's too low; Raising to 30.\n"));
|
|
Packit |
ddac9e |
lu = 30;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else if (lu != 0 && !is_in_range(60, lu, 180))
|
|
Packit |
ddac9e |
log_notice(_("Timeout should be from 60..180 seconds.\n"));
|
|
Packit |
ddac9e |
opt.timeout = (unsigned) lu;
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case 'V': opt.Version = true; break;
|
|
Packit |
ddac9e |
case CODESET_LONGOPT:
|
|
Packit |
ddac9e |
if (!is_rfc1413_token(optarg))
|
|
Packit |
ddac9e |
handle_error(S_FMT, *argv, LONG_OPTS[i].name, optarg);
|
|
Packit |
ddac9e |
free(opt.codeset); opt.codeset = vstrdup(optarg);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case FN_LONGOPT:
|
|
Packit |
ddac9e |
if (optarg != NULL) {
|
|
Packit |
ddac9e |
lu = strtoul(optarg, &endptr, 10);
|
|
Packit |
ddac9e |
if (lu > UINT_MAX || is_bad_strto(optarg, endptr))
|
|
Packit |
ddac9e |
handle_error(S_FMT, *argv, LONG_OPTS[i].name, optarg);
|
|
Packit |
ddac9e |
else opt.fn = (unsigned) lu;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else opt.fn = UINT_MAX;
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case IDENT_LONGOPT:
|
|
Packit |
ddac9e |
free(opt.ident); opt.ident = vstrdup(optarg, DFL_IDENT);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case LANG_LONGOPT:
|
|
Packit |
ddac9e |
lc = strlen(optarg) == 0 ? NULL : setlocale(LC_ALL, optarg);
|
|
Packit |
ddac9e |
if (lc == NULL)
|
|
Packit |
ddac9e |
handle_error(S_FMT, *argv, LONG_OPTS[i].name, optarg);
|
|
Packit |
ddac9e |
else {
|
|
Packit |
ddac9e |
free(opt.lang); opt.lang = vstrdup(lc);
|
|
Packit |
ddac9e |
debug("LC_ALL = %s\n", opt.lang);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case MAPPED_LONGOPT:
|
|
Packit |
ddac9e |
free(opt.mapped);
|
|
Packit |
ddac9e |
if ((opt.mapped = created_pton_hex(optarg)) == NULL)
|
|
Packit |
ddac9e |
handle_error(S_FMT, *argv, LONG_OPTS[i].name, optarg);
|
|
Packit |
ddac9e |
if (strncmp(opt.mapped, IPV4_HEX_0, strlen(IPV4_HEX_0)) != 0)
|
|
Packit |
ddac9e |
log_notice(_("Mapped lower 32 bits not 0; ignoring.\n"));
|
|
Packit |
ddac9e |
debug("prefix map =%s/%s\n", IPV4_HEX_0, opt.mapped + 8);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case OS_LONGOPT:
|
|
Packit |
ddac9e |
if (optarg == NULL && uname(&os) != 0) handle_error(NULL);
|
|
Packit |
ddac9e |
free(opt.os); opt.os = vstrdup(optarg, os.sysname);
|
|
Packit |
ddac9e |
if (!is_rfc1413_token(opt.os))
|
|
Packit |
ddac9e |
handle_error(S_FMT, *argv, LONG_OPTS[i].name, optarg);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case PASSWD_LONGOPT:
|
|
Packit |
ddac9e |
free(opt.passwd); opt.passwd = vstrdup(optarg, DFL_PASSWD);
|
|
Packit |
ddac9e |
if (strlen(opt.passwd) == 0)
|
|
Packit |
ddac9e |
handle_error(S_FMT, *argv, LONG_OPTS[i].name, optarg);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case USERNAME_LONGOPT:
|
|
Packit |
ddac9e |
free(opt.username);
|
|
Packit |
ddac9e |
opt.username = vstrdup(optarg, DFL_USERNAME);
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
default:
|
|
Packit |
ddac9e |
assert(0 == c); // GNU extensions ('\1' and ':') should be off
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case '?': exit(EXIT_FAILURE);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (optind < argc)
|
|
Packit |
ddac9e |
opt.timeout = 0; // don't get client input if request(s) in cmd
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static const char *const DELIM = ",: \t\r\n\v\f";
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static unsigned long long get_tok_ullong(char *s, unsigned base) {
|
|
Packit |
ddac9e |
unsigned long long ull = ULLONG_MAX;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(base <= 36);
|
|
Packit |
ddac9e |
if ((s = strtok(s, DELIM)) != NULL) {
|
|
Packit |
ddac9e |
char *endptr;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
ull = strtoull(s, &endptr, (int) base);
|
|
Packit |
ddac9e |
if ((errno == ERANGE && ull == ULLONG_MAX) || is_bad_strto(s, endptr))
|
|
Packit |
ddac9e |
errno = EINVAL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else errno = EINVAL;
|
|
Packit |
ddac9e |
return ull;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static unsigned long get_tok_uint(char *s, unsigned base) {
|
|
Packit |
ddac9e |
unsigned long ul = ULONG_MAX;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(base <= 36);
|
|
Packit |
ddac9e |
if ((s = strtok(s, DELIM)) != NULL) {
|
|
Packit |
ddac9e |
char *endptr;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
ul = strtoul(s, &endptr, (int) base);
|
|
Packit |
ddac9e |
if (ul > UINT_MAX || is_bad_strto(s, endptr))
|
|
Packit |
ddac9e |
errno = EINVAL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else errno = EINVAL;
|
|
Packit |
ddac9e |
return ul;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static long get_tok_int(char *s, unsigned base) {
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
long l = LONG_MAX;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(base <= 36);
|
|
Packit |
ddac9e |
if ((s = strtok(s, DELIM)) != NULL) {
|
|
Packit |
ddac9e |
char *endptr;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
l = strtol(s, &endptr, (int) base);
|
|
Packit |
ddac9e |
if (l > INT_MAX || is_bad_strto(s, endptr))
|
|
Packit |
ddac9e |
errno = EINVAL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else errno = EINVAL;
|
|
Packit |
ddac9e |
return l;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void destroy_opt(void) {
|
|
Packit |
ddac9e |
free(opt.codeset); free(opt.Encrypt); free(opt.ident); free(opt.lang);
|
|
Packit |
ddac9e |
free(opt.Noident); free(opt.os); free(opt.passwd); free(opt.mapped);
|
|
Packit |
ddac9e |
free(opt.username);
|
|
Packit |
ddac9e |
memset(&opt, 0, sizeof(opt));
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
typedef struct {
|
|
Packit |
ddac9e |
unsigned long lport, rport;
|
|
Packit |
ddac9e |
char *laddr, *raddr;
|
|
Packit |
ddac9e |
} request_t;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
typedef int (*getsockaddr_t)(int, struct sockaddr *, socklen_t *);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *created_connected_addr(int sockfd, getsockaddr_t getXXXXname) {
|
|
Packit |
ddac9e |
struct sockaddr_storage name; socklen_t namelen = sizeof(name);
|
|
Packit |
ddac9e |
char *addr_hex = NULL;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (getXXXXname(sockfd, (struct sockaddr *) &name, &namelen) == 0) {
|
|
Packit |
ddac9e |
size_t addr_size = 0;
|
|
Packit |
ddac9e |
const unsigned char *addr = NULL;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
switch (name.ss_family) {
|
|
Packit |
ddac9e |
struct sockaddr_in *sin; struct sockaddr_in6 *sin6;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
case AF_INET:
|
|
Packit |
ddac9e |
sin = (struct sockaddr_in *) &nam;;
|
|
Packit |
ddac9e |
addr_size = sizeof(sin->sin_addr.s_addr);
|
|
Packit |
ddac9e |
addr = (unsigned char *) &sin->sin_addr.s_addr;
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
case AF_INET6:
|
|
Packit |
ddac9e |
sin6 = (struct sockaddr_in6 *) &nam;;
|
|
Packit |
ddac9e |
addr_size = sizeof(sin6->sin6_addr.s6_addr);
|
|
Packit |
ddac9e |
addr = (unsigned char *) sin6->sin6_addr.s6_addr;
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
default:
|
|
Packit |
ddac9e |
errno = EAFNOSUPPORT;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
addr_hex = created_addr_hex(addr, addr_size);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else if (errno == ENOTSOCK)
|
|
Packit |
ddac9e |
errno = 0; // no error if invoked from cmdline
|
|
Packit |
ddac9e |
return addr_hex;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static request_t created_request(int argc, char *argv[]) {
|
|
Packit |
ddac9e |
request_t in;
|
|
Packit |
ddac9e |
char *line = NULL; size_t n = 0;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(argv != NULL && argc >= 1);
|
|
Packit |
ddac9e |
if (optind >= argc) {
|
|
Packit |
ddac9e |
if (fflush_unlocked(stdout) == EOF) handle_error(NULL);
|
|
Packit |
ddac9e |
in.laddr = created_connected_addr(STDIN_FILENO, getsockname);
|
|
Packit |
ddac9e |
in.raddr = created_connected_addr(STDIN_FILENO, getpeername);
|
|
Packit |
ddac9e |
if (opt.timeout != UINT_MAX)
|
|
Packit |
ddac9e |
alarm(opt.timeout);
|
|
Packit |
ddac9e |
if (getline(&line, &n, stdin) == (ssize_t) -1) {
|
|
Packit |
ddac9e |
if (feof_unlocked(stdin)) {
|
|
Packit |
ddac9e |
free(line);
|
|
Packit |
ddac9e |
exit(EXIT_SUCCESS);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
assert(ferror(stdin));
|
|
Packit |
ddac9e |
handle_error(NULL);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
alarm(0);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else {
|
|
Packit |
ddac9e |
line = vstrdup(argv[(size_t) optind++]);
|
|
Packit |
ddac9e |
in.laddr = in.raddr = NULL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
in.lport = get_tok_uint(line, 10); in.rport = get_tok_uint(NULL, 10);
|
|
Packit |
ddac9e |
debug("local_address =%s:%04lX\n", in.laddr, in.lport);
|
|
Packit |
ddac9e |
debug("rem_address =%s:%04lX\n", in.raddr, in.rport);
|
|
Packit |
ddac9e |
free(line);
|
|
Packit |
ddac9e |
return in;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *created_hostname(const char *node) {
|
|
Packit |
ddac9e |
char *s;
|
|
Packit |
ddac9e |
struct addrinfo *res, hints = { ai_flags: AI_CANONNAME };
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(node != NULL && *node != '\0');
|
|
Packit |
ddac9e |
if (opt.resolve && getaddrinfo(node, NULL, &hints, &res) == 0) {
|
|
Packit |
ddac9e |
s = vstrdup(res->ai_canonname, node); freeaddrinfo(res);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else s = vstrdup(node);
|
|
Packit |
ddac9e |
return s;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static const unsigned long PORT_MIN = 1, PORT_MAX = 65535;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *created_servicename(unsigned long port) {
|
|
Packit |
ddac9e |
char *s;
|
|
Packit |
ddac9e |
const char *const PROTO = "tcp", *const PORT_FMT = "%lu";
|
|
Packit |
ddac9e |
const int NS_PORT = (int) htons((uint16_t) port);
|
|
Packit |
ddac9e |
struct servent *res = opt.resolve ? getservbyport(NS_PORT, PROTO) : NULL;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(is_in_range(PORT_MIN, port, PORT_MAX));
|
|
Packit |
ddac9e |
if (res == NULL || res->s_name == NULL || *res->s_name == '\0') {
|
|
Packit |
ddac9e |
if (asprintf(&s, PORT_FMT, port) < 0) handle_error(NULL);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else s = vstrdup(res->s_name);
|
|
Packit |
ddac9e |
return s;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *get_created_tok_addr(const char *peer_addr_hex) {
|
|
Packit |
ddac9e |
const char *const IPV6_0 = ":0000", *const IPV6_0_RUN = "::";
|
|
Packit |
ddac9e |
const size_t IPV6_SIZE_MAX = INET6_ADDRSTRLEN + sizeof '\0';
|
|
Packit |
ddac9e |
const size_t IPV6_0_RUN_LEN = strlen(IPV6_0_RUN);
|
|
Packit |
ddac9e |
char *addr = calloc(sizeof ':' + IPV6_SIZE_MAX, sizeof(char));
|
|
Packit |
ddac9e |
char *addr_hex, *p = addr, *q, *zero = NULL; size_t zero_n = 0;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (addr != NULL) {
|
|
Packit |
ddac9e |
if ((addr_hex = strtok(NULL, DELIM)) != NULL) {
|
|
Packit |
ddac9e |
size_t z = strlen(addr_hex); const bool IS_IPV6 = z > 8;
|
|
Packit |
ddac9e |
bool is_16_bits = true;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (peer_addr_hex != NULL) {
|
|
Packit |
ddac9e |
char peer_128[HEX_LEN_MAX + sizeof '\0'];
|
|
Packit |
ddac9e |
char host_128[HEX_LEN_MAX + sizeof '\0'];
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(opt.mapped != NULL);
|
|
Packit |
ddac9e |
strcpy(peer_128, opt.mapped); strcpy(host_128, opt.mapped);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
/*
|
|
Packit |
ddac9e |
If mapping IPV4 to IPV6 space is enabled,
|
|
Packit |
ddac9e |
take only last 4 numbers of IPV6
|
|
Packit |
ddac9e |
*/
|
|
Packit |
ddac9e |
if(opt.mapped[0]) {
|
|
Packit |
ddac9e |
strncpy(host_128, addr_hex+z-8, 8);
|
|
Packit |
ddac9e |
strncpy(peer_128, peer_addr_hex, 8);
|
|
Packit |
ddac9e |
} else {
|
|
Packit |
ddac9e |
strncpy(host_128, addr_hex, z);
|
|
Packit |
ddac9e |
strncpy(peer_128, peer_addr_hex, strlen(peer_addr_hex));
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (strcmp(peer_128, host_128) != 0) return addr;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
// hex addr must have even number of digits
|
|
Packit |
ddac9e |
if ((int) z & 1) {
|
|
Packit Service |
d5c571 |
free(addr);
|
|
Packit Service |
d5c571 |
errno = EINVAL;
|
|
Packit Service |
d5c571 |
return NULL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
while (z > 1) {
|
|
Packit |
ddac9e |
unsigned long ul; char *endptr;
|
|
Packit |
ddac9e |
const bool IS_IPV4 = (opt.hybrid && z < 9) || !IS_IPV6;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
addr_hex[z] = '\0'; z -= HEX_DIG;
|
|
Packit |
ddac9e |
ul = strtoul(addr_hex + z, &endptr, 16);
|
|
Packit |
ddac9e |
if (is_bad_strto(addr_hex + z, endptr)) {
|
|
Packit Service |
d5c571 |
free(addr);
|
|
Packit Service |
d5c571 |
errno = EINVAL;
|
|
Packit Service |
d5c571 |
return NULL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if ((!IS_IPV4 || 6 == z) && is_16_bits)
|
|
Packit |
ddac9e |
*p++ = ':';
|
|
Packit |
ddac9e |
if (IS_IPV4)
|
|
Packit |
ddac9e |
p += sprintf(p, "%lu", ul);
|
|
Packit |
ddac9e |
else p += sprintf(p, "%.*lX", HEX_DIG, ul);
|
|
Packit |
ddac9e |
if (IS_IPV4 && z != 0)
|
|
Packit |
ddac9e |
*p++ = '.';
|
|
Packit |
ddac9e |
is_16_bits = !is_16_bits;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
// compress 16-bit runs of zeros
|
|
Packit |
ddac9e |
for (p = strstr(addr, IPV6_0); p != NULL; p = strstr(q, IPV6_0)) {
|
|
Packit |
ddac9e |
size_t count = 0; const size_t IPV6_0_LEN = strlen(IPV6_0);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
for (q = p; strncmp(q, IPV6_0, IPV6_0_LEN) == 0; q += IPV6_0_LEN)
|
|
Packit |
ddac9e |
count += IPV6_0_LEN;
|
|
Packit |
ddac9e |
if (count > zero_n) {
|
|
Packit |
ddac9e |
zero_n = count; zero = p;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (zero_n >= IPV6_0_RUN_LEN) {
|
|
Packit |
ddac9e |
memcpy(zero, IPV6_0_RUN, IPV6_0_RUN_LEN);
|
|
Packit |
ddac9e |
zero += IPV6_0_RUN_LEN; zero_n -= IPV6_0_RUN_LEN;
|
|
Packit |
ddac9e |
if (zero[zero_n] == ':')
|
|
Packit |
ddac9e |
zero_n++;
|
|
Packit |
ddac9e |
memmove(zero, zero + zero_n, strlen(zero + zero_n) + 1);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
// compress four digit hex sequences by removing leading zeros
|
|
Packit |
ddac9e |
while ((zero = strstr(addr, ":0")) != NULL)
|
|
Packit |
ddac9e |
if (strchr(++zero, ':') == NULL) break;
|
|
Packit |
ddac9e |
else memmove(zero, zero + 1, strlen(zero));
|
|
Packit |
ddac9e |
if (addr[1] != ':')
|
|
Packit |
ddac9e |
memmove(addr, addr + 1, IPV6_SIZE_MAX); // rm ':' prefix
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
return addr;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static bool is_regular_file(const char *dir, const char *name) {
|
|
Packit |
ddac9e |
bool is_existing = false;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(dir != NULL);
|
|
Packit |
ddac9e |
if (name != NULL) {
|
|
Packit |
ddac9e |
struct stat st;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode)) {
|
|
Packit |
ddac9e |
char *s;
|
|
Packit |
ddac9e |
const char *const PATHNAME_FMT = "%s/%s";
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (*name != '\0') {
|
|
Packit |
ddac9e |
if (asprintf(&s, PATHNAME_FMT, dir, name) < 0) return false;
|
|
Packit |
ddac9e |
is_existing = stat(s, &st) == 0 && S_ISREG(st.st_mode);
|
|
Packit |
ddac9e |
free(s);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else errno = EISDIR;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else errno = ENOTDIR;
|
|
Packit |
ddac9e |
if (errno == ENOENT)
|
|
Packit |
ddac9e |
errno = 0; // don't report X-ERRNO-%d
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
return is_existing;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
typedef struct {
|
|
Packit |
ddac9e |
bool error;
|
|
Packit |
ddac9e |
unsigned lport, rport; char *os, *enc, *s;
|
|
Packit |
ddac9e |
} reply_t;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static const size_t USERID_MAX_LEN = 512; // as per RFC1413 <octet-string>
|
|
Packit |
ddac9e |
static const char *const TEXT_READ_MODE = "r"; // passphrase, /proc plaintext
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static bool get_info(reply_t *out, request_t in, const char *tcpname) {
|
|
Packit |
ddac9e |
//unsigned long lport, rport, uid, status; FILE *stream;
|
|
Packit |
ddac9e |
unsigned long lport, rport, status; FILE *stream;
|
|
Packit |
ddac9e |
long uid;
|
|
Packit |
ddac9e |
const unsigned long ESTABLISHED = 0x01;
|
|
Packit |
ddac9e |
unsigned lineno = 0;
|
|
Packit |
ddac9e |
char *laddr = NULL, *raddr = NULL;
|
|
Packit |
ddac9e |
bool is_port_pair_found = false;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
char *created_verbose(const char *name, unsigned long id) {
|
|
Packit |
ddac9e |
size_t n; time_t tod;
|
|
Packit |
ddac9e |
char *s, when[USERID_MAX_LEN], *host1, *port1, *host2, *port2;
|
|
Packit |
ddac9e |
const char *const UTC_FMT = "%FT%TZ", *const TZ_FMT = "(%a %EX %z/%Z)";
|
|
Packit |
ddac9e |
const char *const VERBOSE_FMT = "%s:%lu,%s,%s|%s,%s|%s";
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (time(&tod) == (time_t) -1) handle_error(NULL);
|
|
Packit |
ddac9e |
n = strftime(when, sizeof(when), UTC_FMT, gmtime(&tod));
|
|
Packit |
ddac9e |
strftime(when + n, sizeof(when) - n, TZ_FMT, localtime(&tod));
|
|
Packit |
ddac9e |
host1 = created_hostname(laddr); port1 = created_servicename(lport);
|
|
Packit |
ddac9e |
host2 = created_hostname(raddr); port2 = created_servicename(rport);
|
|
Packit |
ddac9e |
asprintf(&s, VERBOSE_FMT, name, id, when, host1, port1, host2, port2);
|
|
Packit |
ddac9e |
free(host1); free(port1); free(host2); free(port2);
|
|
Packit |
ddac9e |
return s;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(out != NULL);
|
|
Packit |
ddac9e |
if (tcpname == NULL) return false;
|
|
Packit |
ddac9e |
if ((stream = fopen(tcpname, TEXT_READ_MODE)) == NULL) {
|
|
Packit |
ddac9e |
// some systems won't have tcp6 (and rarely, plain IPv4 tcp) files in
|
|
Packit |
ddac9e |
// /proc/net. The absence of either file isn't an error; although
|
|
Packit |
ddac9e |
// the lack of both prevents authd from looking up anything
|
|
Packit |
ddac9e |
if (errno == ENOENT) {
|
|
Packit |
ddac9e |
if (opt.log) log_notice("%s: %s\n", tcpname, strerror(errno));
|
|
Packit |
ddac9e |
errno = 0;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else out->error = true;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else for (;;) {
|
|
Packit |
ddac9e |
unsigned long sl, inode;
|
|
Packit |
ddac9e |
uid_t euid;
|
|
Packit |
ddac9e |
char line[PROC_MAX_LEN], tmp[PROC_MAX_LEN];
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
// FIXME: setting the buffer to be larger than normal is done to
|
|
Packit |
ddac9e |
// lessen (but not eliminate) the chance of the proc file changing
|
|
Packit |
ddac9e |
// while in the middle of reading it
|
|
Packit |
ddac9e |
if (0 == lineno) {
|
|
Packit |
ddac9e |
const size_t SIZE = (size_t) (BUFSIZ * PROC_SIZE);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (setvbuf(stream, NULL, _IOFBF, SIZE) != 0) {
|
|
Packit |
ddac9e |
out->error = true; break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (fgets_unlocked(line, (int) PROC_MAX_LEN, stream) == NULL) {
|
|
Packit |
ddac9e |
if (ferror_unlocked(stream))
|
|
Packit |
ddac9e |
out->error = true;
|
|
Packit |
ddac9e |
assert(feof(stream));
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else lineno++;
|
|
Packit |
ddac9e |
// first line of /proc/net/tcp* is a header that looks similar to:
|
|
Packit |
ddac9e |
// sl local rem st tx rx tr tm->when retrnsmt uid timeout inode
|
|
Packit |
ddac9e |
if (1 == lineno) continue;
|
|
Packit |
ddac9e |
// sample line from tcp (tcp6 just has a bigger ip address):
|
|
Packit |
ddac9e |
// 0: 00000000:800B 00000000:0000 0A 0000:0000 00:0000 0000 0000 ...
|
|
Packit |
ddac9e |
sl = get_tok_uint(strcpy(tmp, line), 10); // sl (base 10 uint)
|
|
Packit |
ddac9e |
laddr = get_created_tok_addr(in.laddr); // local (little endian hex)
|
|
Packit |
ddac9e |
lport = get_tok_uint(NULL, 16); // ... local port
|
|
Packit |
ddac9e |
raddr = get_created_tok_addr(in.raddr); // remote (little endian hex)
|
|
Packit |
ddac9e |
rport = get_tok_uint(NULL, 16); // ... remote port
|
|
Packit |
ddac9e |
status = get_tok_uint(NULL, 16); // status (01 = ESTABLISHED)
|
|
Packit |
ddac9e |
(void) get_tok_uint(NULL, 16); // tx_queue
|
|
Packit |
ddac9e |
(void) get_tok_uint(NULL, 16); // rx_queue
|
|
Packit |
ddac9e |
(void) get_tok_uint(NULL, 16); // tr (boolean)
|
|
Packit |
ddac9e |
(void) get_tok_ullong(NULL, 16); // tm->when (unit: jiffies)
|
|
Packit |
ddac9e |
strtok(NULL, DELIM); // retrnsmt
|
|
Packit |
ddac9e |
//uid = get_tok_uint(NULL, 10); // uid (base 10 uint)
|
|
Packit |
ddac9e |
uid = get_tok_int(NULL, 10); // uid (base 10 int)
|
|
Packit |
ddac9e |
strtok(NULL, DELIM); // timeout
|
|
Packit |
ddac9e |
inode = get_tok_uint(NULL, 10); // inode (base 10 uint)
|
|
Packit |
ddac9e |
if (errno == EINVAL) {
|
|
Packit |
ddac9e |
// format of /proc/net/tcp* has changed or it's a bogus file
|
|
Packit |
ddac9e |
debug("%s:%u: %s", tcpname, lineno, line); // XXX: NL terminated?
|
|
Packit |
ddac9e |
out->error = true; errno = 0;
|
|
Packit |
ddac9e |
out->s = opt.xerror ? vstrdup("X-PROC") : NULL; break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else if (errno != 0) {
|
|
Packit |
ddac9e |
// most likely an out-of-memory error
|
|
Packit |
ddac9e |
out->error = true; break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else if (*laddr == '\0' || *raddr == '\0') {
|
|
Packit |
ddac9e |
// host address doesn't match peer address, so skip it
|
|
Packit |
ddac9e |
free(laddr); free(raddr); laddr = raddr = NULL; continue;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else euid = (uid_t) uid;
|
|
Packit |
ddac9e |
if (lport == in.lport && rport == in.rport && status == ESTABLISHED) {
|
|
Packit |
ddac9e |
struct passwd *pwd = getpwuid(euid);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
is_port_pair_found = true;
|
|
Packit |
ddac9e |
debug("%-14s=sl:%lu uid:%lu inode:%lu\n", tcpname, sl, uid, inode);
|
|
Packit |
ddac9e |
if (opt.username != NULL) {
|
|
Packit |
ddac9e |
if ((pwd = getpwnam(opt.username)) == NULL) {
|
|
Packit |
ddac9e |
out->error = true;
|
|
Packit |
ddac9e |
out->s = opt.xerror ? vstrdup("X-NAME") : NULL; break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else euid = pwd->pw_uid;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (pwd == NULL) { // passwd db doesn't have uid in /proc/net/tcp*
|
|
Packit |
ddac9e |
out->error = true;
|
|
Packit |
ddac9e |
out->s = opt.xerror ? vstrdup("X-UID") : NULL; break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else {
|
|
Packit |
ddac9e |
bool hidden = opt.ident != NULL;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (is_regular_file(pwd->pw_dir, opt.ident))
|
|
Packit |
ddac9e |
hidden = false;
|
|
Packit |
ddac9e |
if (is_regular_file(pwd->pw_dir, opt.Noident))
|
|
Packit |
ddac9e |
hidden = true;
|
|
Packit |
ddac9e |
if (errno) {
|
|
Packit |
ddac9e |
out->error = true;
|
|
Packit |
ddac9e |
out->s = opt.xerror ? vstrdup("X-FILE") : NULL;
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else if (hidden) {
|
|
Packit |
ddac9e |
out->error = true; out->s = vstrdup("HIDDEN-USER"); break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
out->s = vstrdup(pwd->pw_name);
|
|
Packit |
ddac9e |
if (opt.number) {
|
|
Packit |
ddac9e |
const char *const UID_FMT = "%lu";
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
free(out->s);
|
|
Packit |
ddac9e |
if (asprintf(&out->s, UID_FMT, (unsigned long) euid) < 0) {
|
|
Packit |
ddac9e |
out->error = true; break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (opt.fn != 0 && *pwd->pw_gecos != '\0') {
|
|
Packit |
ddac9e |
char *s = vstrdup(pwd->pw_gecos);
|
|
Packit |
ddac9e |
size_t n = 0;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
// gecos field usually just has 1 field, the full name--
|
|
Packit |
ddac9e |
// but sometimes the user's office, office phone and home
|
|
Packit |
ddac9e |
// phone will be in csv format
|
|
Packit |
ddac9e |
for (unsigned u = 0; u < opt.fn && s[n] != '\0'; u++, n++)
|
|
Packit |
ddac9e |
n += strcspn(s + n, ",");
|
|
Packit |
ddac9e |
s[--n] = '\0';
|
|
Packit |
ddac9e |
if (opt.number) {
|
|
Packit |
ddac9e |
n = strcspn(s, ",");
|
|
Packit |
ddac9e |
memmove(s, s + n, strlen(s + n) + 1);
|
|
Packit |
ddac9e |
n = strlen(out->s) + strlen(s);
|
|
Packit |
ddac9e |
if ((out->s = realloc(out->s, ++n)) == NULL) {
|
|
Packit |
ddac9e |
out->error = true; break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
strcat(out->s, s); free(s);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else { free(out->s); out->s = s; }
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (opt.verbose && !out->error) {
|
|
Packit |
ddac9e |
char *brief = out->s;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if ((out->s = created_verbose(brief, uid)) == NULL)
|
|
Packit |
ddac9e |
out->error = true;
|
|
Packit |
ddac9e |
else free(brief);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else { free(laddr); free(raddr); laddr = raddr = NULL; }
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
free(laddr); free(raddr);
|
|
Packit |
ddac9e |
if (stream != NULL && fclose(stream) == EOF) {
|
|
Packit |
ddac9e |
out->error = true; out->s = NULL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
return is_port_pair_found;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
typedef struct {
|
|
Packit |
ddac9e |
const EVP_CIPHER *cipher; BIO *writer; BUF_MEM *buffer;
|
|
Packit |
ddac9e |
unsigned char *key, *iv, salt[PKCS5_SALT_LEN];
|
|
Packit |
ddac9e |
} crypto_t;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static bool initialize_crypto(crypto_t *x, const char *filename) {
|
|
Packit |
ddac9e |
struct stat file;
|
|
Packit |
ddac9e |
bool is_initialized = false;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(filename != NULL && x != NULL);
|
|
Packit |
ddac9e |
if (stat(filename, &file) == 0) {
|
|
Packit |
ddac9e |
FILE *stream; ssize_t len;
|
|
Packit |
ddac9e |
const EVP_MD *const HASH = EVP_md5(); // openssl compat: enc -pass
|
|
Packit |
ddac9e |
const size_t KEY_SIZE = EVP_CIPHER_key_length(x->cipher);
|
|
Packit |
ddac9e |
const size_t IV_SIZE = EVP_CIPHER_iv_length(x->cipher);
|
|
Packit Service |
d5c571 |
unsigned char *pass = NULL; size_t z = 0;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (!S_ISREG(file.st_mode)) return false; // no dirs, devs, etc.
|
|
Packit |
ddac9e |
if (file.st_mode & (S_IROTH | S_IWOTH)) return false; // no ------rw-
|
|
Packit |
ddac9e |
if ((x->key = malloc(KEY_SIZE)) == NULL) return false;
|
|
Packit |
ddac9e |
if ((x->iv = malloc(IV_SIZE)) == NULL) return false;
|
|
Packit |
ddac9e |
if ((stream = fopen(filename, TEXT_READ_MODE)) == NULL) return false;
|
|
Packit |
ddac9e |
if ((len = getline(&pass, &z, stream)) == (ssize_t) -1) return false;
|
|
Packit |
ddac9e |
if (fclose(stream) == EOF) return false;
|
|
Packit |
ddac9e |
if (len > 0 && pass[(size_t) (len - 1)] == '\n')
|
|
Packit |
ddac9e |
pass[(size_t) --len] = '\0';
|
|
Packit |
ddac9e |
if (RAND_pseudo_bytes(x->salt, sizeof(x->salt)) <= 0) return false;
|
|
Packit |
ddac9e |
EVP_BytesToKey(x->cipher, HASH, x->salt, pass, len, 1, x->key, x->iv);
|
|
Packit |
ddac9e |
memset(pass, 0, len); // XXX: crypto erase
|
|
Packit |
ddac9e |
free(pass);
|
|
Packit |
ddac9e |
is_initialized = true;
|
|
Packit |
ddac9e |
if (opt.debug) {
|
|
Packit |
ddac9e |
char line[80] = { '\0' }, *p = line;
|
|
Packit |
ddac9e |
int n; size_t size = sizeof(line);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
n = snprintf(p, size, "salt[%2zu bytes]=", sizeof(x->salt));
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
for (z = 0; z < sizeof(x->salt) && n < (int) size; z++) {
|
|
Packit |
ddac9e |
p += n; size -= n;
|
|
Packit |
ddac9e |
n = snprintf(p, size, "%.*hhX", CHAR_BIT / 4, x->salt[z]);
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
debug("%s\n", line); p = line; size = sizeof(line);
|
|
Packit |
ddac9e |
n = snprintf(p, size, "key[%4d bits]=", (int) KEY_SIZE * CHAR_BIT);
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
for (z = 0; z < KEY_SIZE && n < (int) size; z++) {
|
|
Packit |
ddac9e |
p += n; size -= n;
|
|
Packit |
ddac9e |
n = snprintf(p, size, "%.*hhX", CHAR_BIT / 4, x->key[z]);
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
debug("%s\n", line); p = line; size = sizeof(line);
|
|
Packit |
ddac9e |
n = snprintf(p, size, "iv[%5d bits]=", (int) IV_SIZE * CHAR_BIT);
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
for (z = 0; z < IV_SIZE && n < (int) size; z++) {
|
|
Packit |
ddac9e |
p += n; size -= n;
|
|
Packit |
ddac9e |
n = snprintf(p, size, "%.*hhX", CHAR_BIT / 4, x->iv[z]);
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
debug("%s\n", line);
|
|
Packit |
ddac9e |
memset(line, 0, sizeof(line)); // XXX: crypto erase
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
return is_initialized;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static char *created_ciphertext_b64(const char *s) {
|
|
Packit |
ddac9e |
BIO *encoder; char *b64;
|
|
Packit |
ddac9e |
const char *const MAGIC = "Salted__"; // openssl compat: enc -salt
|
|
Packit |
ddac9e |
const char *const NL = "\n"; // is strcat()ed to plaintext
|
|
Packit |
ddac9e |
crypto_t x = { cipher: NULL };
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
assert(s != NULL);
|
|
Packit |
ddac9e |
OpenSSL_add_all_ciphers();
|
|
Packit |
ddac9e |
if ((x.cipher = EVP_get_cipherbyname(opt.Encrypt)) == NULL) return NULL;
|
|
Packit |
ddac9e |
debug("cipher name =%s\n", EVP_CIPHER_name(x.cipher));
|
|
Packit |
ddac9e |
debug("cipher block =%d bytes\n", EVP_CIPHER_block_size(x.cipher));
|
|
Packit |
ddac9e |
switch (EVP_CIPHER_mode(x.cipher)) {
|
|
Packit |
ddac9e |
case EVP_CIPH_ECB_MODE: debug("cipher mode =%s\n", "ECB"); break;
|
|
Packit |
ddac9e |
case EVP_CIPH_CBC_MODE: debug("cipher mode =%s\n", "CBC"); break;
|
|
Packit |
ddac9e |
case EVP_CIPH_CFB_MODE: debug("cipher mode =%s\n", "CFB"); break;
|
|
Packit |
ddac9e |
case EVP_CIPH_OFB_MODE: debug("cipher mode =%s\n", "OFB"); break;
|
|
Packit |
ddac9e |
case EVP_CIPH_STREAM_CIPHER: debug("cipher mode =%s\n", "stream");
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (!initialize_crypto(&x, opt.passwd)) return NULL;
|
|
Packit |
ddac9e |
if ((encoder = BIO_new(BIO_s_mem())) == NULL) return NULL;
|
|
Packit |
ddac9e |
else x.writer = encoder;
|
|
Packit |
ddac9e |
if ((encoder = BIO_new(BIO_f_base64())) == NULL) return NULL;
|
|
Packit |
ddac9e |
else {
|
|
Packit |
ddac9e |
BIO_set_flags(encoder, BIO_FLAGS_BASE64_NO_NL);
|
|
Packit |
ddac9e |
x.writer = BIO_push(encoder, x.writer);
|
|
Packit |
ddac9e |
if (BIO_write(x.writer, MAGIC, strlen(MAGIC)) <= 0) return NULL;
|
|
Packit |
ddac9e |
if (BIO_write(x.writer, x.salt, sizeof(x.salt)) <= 0) return NULL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if ((encoder = BIO_new(BIO_f_cipher())) == NULL) return NULL;
|
|
Packit |
ddac9e |
else {
|
|
Packit |
ddac9e |
BIO_set_cipher(encoder, x.cipher, x.key, x.iv, true);
|
|
Packit |
ddac9e |
x.writer = BIO_push(encoder, x.writer);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (BIO_write(x.writer, s, strlen(s)) <= 0) return NULL;
|
|
Packit |
ddac9e |
if (BIO_write(x.writer, NL, strlen(NL)) <= 0) return NULL;
|
|
Packit |
ddac9e |
if (BIO_flush(x.writer) <= 0) return NULL;
|
|
Packit |
ddac9e |
BIO_get_mem_ptr(x.writer, &x.buffer);
|
|
Packit |
ddac9e |
if ((b64 = calloc(x.buffer->length + 3, sizeof(char))) != NULL) {
|
|
Packit |
ddac9e |
b64[0] = '['; // pidentd compat: base64 in brackets
|
|
Packit |
ddac9e |
memcpy(b64 + 1, x.buffer->data, x.buffer->length);
|
|
Packit |
ddac9e |
b64[x.buffer->length + 1] = ']';
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
BIO_free_all(x.writer); EVP_cleanup();
|
|
Packit |
ddac9e |
free(x.key); free(x.iv);
|
|
Packit |
ddac9e |
memset(&x, 0, sizeof(x)); // XXX: crypto erase
|
|
Packit |
ddac9e |
return b64;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static reply_t created_reply(request_t in) {
|
|
Packit |
ddac9e |
reply_t out = { lport: 0, rport: 0 };
|
|
Packit |
ddac9e |
bool is_invalid_port = false;
|
|
Packit |
ddac9e |
unsigned attempts = 0;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
out.os = vstrdup(opt.other ? "OTHER" : opt.os, DFL_OS);
|
|
Packit |
ddac9e |
out.enc = vstrdup(opt.codeset, nl_langinfo(CODESET), "X-UNKNOWN");
|
|
Packit |
ddac9e |
if (!is_in_range(PORT_MIN, in.lport, PORT_MAX)) {
|
|
Packit |
ddac9e |
in.lport = 0; is_invalid_port = true; errno = 0;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (!is_in_range(PORT_MIN, in.rport, PORT_MAX)) {
|
|
Packit |
ddac9e |
in.rport = 0; is_invalid_port = true; errno = 0;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
out.lport = (unsigned) in.lport; out.rport = (unsigned) in.rport;
|
|
Packit |
ddac9e |
if (is_invalid_port) {
|
|
Packit |
ddac9e |
out.s = vstrdup("INVALID-PORT"); out.error = true;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
// FIXME: because the /proc/net/tcp*'s updated asyncronously, it's possible
|
|
Packit |
ddac9e |
// to miss reading a pair when reading syncronously (even with a locked
|
|
Packit |
ddac9e |
// read & a large enough buffer). To lessen the chance of a false negative,
|
|
Packit |
ddac9e |
// inefficiently & unreliably re-read proc more than once if not found.
|
|
Packit |
ddac9e |
else for (;;) {
|
|
Packit |
ddac9e |
// TODO: port pair more likely to be IPv4 than IPv6,
|
|
Packit |
ddac9e |
// so scan IPv4 first for better performance; should be configurable
|
|
Packit |
ddac9e |
if (get_info(&out, in, PROC_V4) || get_info(&out, in, PROC_V6)) break;
|
|
Packit |
ddac9e |
else if (attempts++ < PROC_RETRY) {
|
|
Packit |
ddac9e |
if (usleep(PROC_SLEEP_US) == -1) handle_error(NULL);
|
|
Packit |
ddac9e |
continue;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
out.error = true; errno = 0; out.s = vstrdup("NO-USER"); break;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (!out.error && opt.Encrypt != NULL) {
|
|
Packit |
ddac9e |
char *encrypted = created_ciphertext_b64(out.s);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
memset(out.s, 0, strlen(out.s)); // XXX: crypto erase
|
|
Packit |
ddac9e |
free(out.s);
|
|
Packit |
ddac9e |
if (encrypted == NULL) {
|
|
Packit |
ddac9e |
int n;
|
|
Packit |
ddac9e |
char msg[USERID_MAX_LEN], *p = msg;
|
|
Packit |
ddac9e |
unsigned long code;
|
|
Packit |
ddac9e |
size_t size = USERID_MAX_LEN;
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
out.error = true;
|
|
Packit |
ddac9e |
if (opt.xerror) {
|
|
Packit |
ddac9e |
n = snprintf(p, size, "%s", "X-CRYPTO");
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
do if ((code = ERR_get_error()) != 0) {
|
|
Packit |
ddac9e |
p += n; size = (size_t) n > size ? 0 : size - n;
|
|
Packit |
ddac9e |
n = snprintf(p, size, "-%08lX", code);
|
|
Packit |
ddac9e |
assert(n >= 0);
|
|
Packit |
ddac9e |
} while (code != 0 && (size_t) n < size);
|
|
Packit |
ddac9e |
out.s = vstrdup(msg);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else out.s = NULL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else out.s = encrypted;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
// RFC1413: when the info field isn't usable as-is, set OS to OTHER
|
|
Packit |
ddac9e |
if (opt.fn != 0 || opt.verbose || opt.number || opt.Encrypt != NULL) {
|
|
Packit |
ddac9e |
free(out.os); out.os = vstrdup("OTHER");
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
return out;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void send_reply(reply_t out) {
|
|
Packit |
ddac9e |
char *s; int n; const char *fmt;
|
|
Packit |
ddac9e |
// RFC 1413 says servers shouldn't use unnecessary space, but mimic
|
|
Packit |
ddac9e |
// existing servers' behavior so inflexible clients work
|
|
Packit |
ddac9e |
const char *const GOOD_FMT = "%u , %u : USERID : %s%.0s :%s\r\n";
|
|
Packit |
ddac9e |
const char *const GOOD_NONASCII_FMT = "%u , %u : USERID : %s , %s :%s\r\n";
|
|
Packit |
ddac9e |
const char *const BAD_FMT = "%u , %u : ERROR :%.0s%.0s%s%.0d\r\n";
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
if (out.s == NULL || (opt.error && *out.s != 'X')) {
|
|
Packit |
ddac9e |
out.s = "UNKNOWN-ERROR"; out.error = true;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (strlen(out.s) > USERID_MAX_LEN || strpbrk(out.s, "\r\n") != NULL) {
|
|
Packit |
ddac9e |
out.error = true; out.s = opt.xerror ? "X-RFC1413" : NULL;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
if (out.error) {
|
|
Packit |
ddac9e |
fmt = BAD_FMT;
|
|
Packit |
ddac9e |
if (opt.log && errno != 0) log_warning(NULL);
|
|
Packit |
ddac9e |
if (opt.abrupt) goto abrupt;
|
|
Packit |
ddac9e |
if (opt.xerror && errno != 0 && *out.s != 'X')
|
|
Packit |
ddac9e |
out.s = "X-ERRNO-";
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
else fmt = is_print_ascii(out.s) ? GOOD_FMT : GOOD_NONASCII_FMT;
|
|
Packit |
ddac9e |
n = asprintf(&s, fmt, out.lport, out.rport, out.os, out.enc, out.s, errno);
|
|
Packit |
ddac9e |
if (n < 0) handle_error(NULL);
|
|
Packit |
ddac9e |
if (fputs_unlocked(s, stdout) == EOF) handle_error(NULL);
|
|
Packit |
ddac9e |
else free(s);
|
|
Packit |
ddac9e |
abrupt:
|
|
Packit |
ddac9e |
if (out.error) exit(EXIT_FAILURE); // must exit to clean alloc res
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void destroy_reply(reply_t *out) {
|
|
Packit |
ddac9e |
assert(out != NULL && !out->error);
|
|
Packit |
ddac9e |
free(out->os); free(out->enc); free(out->s); memset(out, 0, sizeof(*out));
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void destroy_request(request_t *in) {
|
|
Packit |
ddac9e |
free(in->laddr); free(in->raddr); memset(in, 0, sizeof(*in));
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
static void catch_signal(int which) {
|
|
Packit |
ddac9e |
_Exit(which == SIGALRM ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
int main(int argc, char *argv[]) {
|
|
Packit |
ddac9e |
# if !defined NDEBUG
|
|
Packit |
ddac9e |
mtrace();
|
|
Packit |
ddac9e |
# endif
|
|
Packit |
ddac9e |
if (setlocale(LC_ALL, "") == NULL) abort();
|
|
Packit |
ddac9e |
errno = 0; /* not an error for some locale files to
|
|
Packit |
ddac9e |
be missing */
|
|
Packit |
ddac9e |
if (signal(SIGALRM, catch_signal) == SIG_ERR) abort();
|
|
Packit |
ddac9e |
if (textdomain(PACKAGE) == NULL) handle_error(NULL);
|
|
Packit |
ddac9e |
if (bindtextdomain(PACKAGE, NULL) == NULL) handle_error(NULL);
|
|
Packit |
ddac9e |
openlog(PACKAGE, LOG_PID, LOG_DAEMON);
|
|
Packit |
ddac9e |
create_opt(argc, argv);
|
|
Packit |
ddac9e |
atexit(destroy_opt);
|
|
Packit |
ddac9e |
if (opt.help) show_help();
|
|
Packit |
ddac9e |
else if (opt.Version) show_version();
|
|
Packit |
ddac9e |
else while (opt.multiquery-- != 0 && (opt.timeout != 0 || optind < argc)) {
|
|
Packit |
ddac9e |
request_t in = created_request(argc, argv);
|
|
Packit |
ddac9e |
reply_t out = created_reply(in);
|
|
Packit |
ddac9e |
|
|
Packit |
ddac9e |
destroy_request(&in);
|
|
Packit |
ddac9e |
send_reply(out);
|
|
Packit |
ddac9e |
destroy_reply(&out;;
|
|
Packit |
ddac9e |
}
|
|
Packit |
ddac9e |
return EXIT_SUCCESS;
|
|
Packit |
ddac9e |
}
|