Blame authd.c

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
}