#include <stdio.h>
#include <string.h>
#include <string.h>
+#include <time.h>
#include <sys/types.h>
+#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
+#include <grp.h>
#include <pwd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <netdb.h>
#include <syslog.h>
#include <mLib/bits.h>
#include <mLib/conn.h>
+#include <mLib/daemonize.h>
#include <mLib/darray.h>
#include <mLib/dstr.h>
#include <mLib/fdflags.h>
#include <mLib/fwatch.h>
+#include <mLib/macros.h>
+#include <mLib/mdwopt.h>
#include <mLib/quis.h>
#include <mLib/report.h>
#include <mLib/sel.h>
#include <mLib/selbuf.h>
+#include <mLib/sig.h>
-/*----- System specifics --------------------------------------------------*/
+/*----- Address family handling -------------------------------------------*/
-#define SYS_UNDEF 0
-#define SYS_LINUX 1
-
-#if SYS == SYS_LINUX
-# include <linux/netlink.h>
-# include <linux/rtnetlink.h>
-#else
-# error "Unsupported operating system: sorry. Patches welcome!"
-#endif
+/* The maximum length of an address formatted as a text string, including the
+ * terminating null byte.
+ */
+#define ADDRLEN 64
-/*----- Data structures ---------------------------------------------------*/
+/* A list of address types. */
+#define ADDRTYPES(_) \
+ _(ipv4, IPV4) \
+ _(ipv6, IPV6)
-#define ADDRLEN 64
+/* Address types for the various families, in the form acceptable to
+ * inet_ntop(3) and inet_pton(3). */
+#define TYPE_IPV4 struct in_addr
+#define TYPE_IPV6 struct in6_addr
+/* A union of address types. */
union addr {
- struct in_addr ipv4;
- struct in6_addr ipv6;
+#define UMEMB(ty, TY) TYPE_##TY ty;
+ ADDRTYPES(UMEMB)
+#undef UMEMB
};
+/* A socket holds an address and a port number. */
struct socket {
- union addr addr;
- unsigned port;
+ union addr addr; /* The address */
+ unsigned port; /* The port, in /host/ byte order */
};
+/* An address pattern consists of an address and a prefix length: the
+ * pattern matches an address if they agree in the first LEN bits.
+ */
struct addrpat {
- unsigned len;
- union addr addr;
+ union addr addr; /* The base address */
+ unsigned len; /* The prefix length */
};
+/* A port pattern matches a port if the port is within the stated (inclusive)
+ * bounds.
+ */
struct portpat {
unsigned lo, hi;
};
+/* A socket pattern consists simply of an address pattern and a port pattern:
+ * it matches a socket componentwise.
+ */
struct sockpat {
struct addrpat addr;
struct portpat port;
};
-#define ADDRTYPES(_) \
- _(ipv4, IPV4) \
- _(ipv6, IPV6)
-
+/* The table of address-type operations. Each address family has one of
+ * these, so that most of the program doesn't need to worry about these
+ * details.
+ */
struct addrops {
- int af;
- const char *name;
- unsigned len;
- const union addr *any;
- const struct addrops_sys *sys;
+ int af; /* The AF_* constant */
+ const char *name; /* Name of the protocol, for logs */
+ unsigned len; /* Length of an address, in bits */
+ const union addr *any; /* A wildcard address */
+ const struct addrops_sys *sys; /* Pointer to system-specific ops */
+
int (*addreq)(const union addr *, const union addr *);
+ /* Return nonzero if the two addresses are equal. */
+
int (*match_addrpat)(const struct addrpat *, const union addr *);
+ /* Return nonzero if the pattern matches the address. */
+
void (*socket_to_sockaddr)(const struct socket *s, void *, size_t *);
+ /* Convert a socket structure to a `struct sockaddr', and return the
+ * size of the latter.
+ */
+
void (*sockaddr_to_addr)(const void *, union addr *);
+ /* Extract the address from a `struct sockaddr'. */
+
int (*init_listen_socket)(int);
+ /* Perform any necessary extra operations on a socket which is going
+ * to be used to listen for incoming connections.
+ */
};
+/* A handy constant for each address family. These are more useful than the
+ * AF_* constants in that they form a dense sequence.
+ */
enum {
#define DEFADDR(ty, TY) ADDR_##TY,
ADDRTYPES(DEFADDR)
ADDR_LIMIT
};
+/* The table of address operations, indexed by the ADDR_* constants defined
+ * just above.
+ */
extern const struct addrops addroptab[];
-#define OPS_SYS(ty, TY) \
+
+/* System-specific operations, provided by the system-specific code for its
+ * own purposes.
+ */
+#define OPS_SYS(ty, TY) \
extern const struct addrops_sys addrops_sys_##ty;
ADDRTYPES(OPS_SYS)
#undef OPS_SYS
+/* Answer whether the sockets SA and SB are equal. */
+extern int sockeq(const struct addrops */*ao*/,
+ const struct socket */*sa*/, const struct socket */*sb*/);
+
+/* Write a textual description of S to the string D. */
+extern void dputsock(dstr */*d*/, const struct addrops */*ao*/,
+ const struct socket */*s*/);
+
+/*----- Queries and responses ---------------------------------------------*/
+
+/* Constants for describing the `L'ocal and `R'emote ends of a connection. */
enum { L, R, NDIR };
+/* Response types, and the data needed to represent any associated data. A
+ * U_(MEMB, TYPE) constructs a union member; an N_ means no associated data.
+ */
#define RESPONSE(_) \
- _(ERROR, U(error, unsigned)) \
- _(UID, U(uid, uid_t)) \
- _(NAT, U(nat, struct socket))
+ _(ERROR, U_(error, unsigned)) \
+ _(UID, U_(uid, uid_t)) \
+ _(NAT, U_(nat, struct socket))
+
+enum {
+#define DEFENUM(what, branch) R_##what,
+ RESPONSE(DEFENUM)
+#undef DEFENUM
+ R_LIMIT
+};
+/* Protocol error tokens. */
#define ERROR(_) \
_(INVPORT, "INVALID-PORT") \
_(NOUSER, "NO-USER") \
_(HIDDEN, "HIDDEN-USER") \
_(UNKNOWN, "UNKNOWN-ERROR")
-extern const char *const errtok[];
enum {
#define DEFENUM(err, tok) E_##err,
E_LIMIT
};
-enum {
-#define DEFENUM(what, branch) R_##what,
- RESPONSE(DEFENUM)
-#undef DEFENUM
- R_LIMIT
-};
+extern const char *const errtok[];
+/* The query structure keeps together the parameters to the client's query
+ * and our response to it.
+ */
struct query {
- const struct addrops *ao;
- struct socket s[NDIR];
- unsigned resp;
- union {
+ const struct addrops *ao; /* Address family operations */
+ struct socket s[NDIR]; /* The local and remote ends */
+ unsigned resp; /* Our response type */
+ union { /* A union of response data */
#define DEFBRANCH(WHAT, branch) branch
-#define U(memb, ty) ty memb;
-#define N
+#define U_(memb, ty) ty memb;
+#define N_
RESPONSE(DEFBRANCH)
-#undef U
-#undef N
+#undef U_
+#undef N_
#undef DEFBRANCH
} u;
} query;
-enum {
- T_OK,
- T_EOL,
- T_EOF,
- T_ERROR
-};
+/*----- Common utility functions ------------------------------------------*/
+
+/* Format and log MSG somewhere sensible, at the syslog(3) priority PRIO.
+ * Prefix it with a description of the query Q, if non-null.
+ */
+extern void PRINTF_LIKE(3, 4)
+ logmsg(const struct query */*q*/, int /*prio*/, const char */*msg*/, ...);
+
+/* Format and report MSG as a fatal error, and exit. */
+extern void PRINTF_LIKE(1, 2) fatal(const char */*msg*/, ...);
+/*----- System-specific connection identification code --------------------*/
+
+/* Find out who is responsible for the connection described in the query Q.
+ * Write the answer to Q. Errors are logged and reported via the query
+ * structure.
+ */
+extern void identify(struct query */*q*/);
+
+/* Fill the buffer at P with SZ random bytes. The buffer will be moderately
+ * large: this is intended to be a low-level interface, not a general-purpose
+ * utility.
+ */
+extern void fill_random(void */*p*/, size_t /*sz*/);
+
+/* Initialize the system-specific code. */
+extern void init_sys(void);
+
+/*----- Policy management -------------------------------------------------*/
+
+/* The possible policy actions and their names. */
#define ACTIONS(_) \
_(USER, "user") \
_(TOKEN, "token") \
A_LIMIT
};
+/* A policy action. */
struct action {
unsigned act;
union {
- unsigned user;
- char *lie;
+ unsigned user; /* Bitmask of permitted actions */
+ char *lie; /* The user name to impersonate */
} u;
};
+/* A user pattern matches a user if the uid is within the given bounds. */
+struct userpat {
+ unsigned lo, hi;
+};
+
+/* A policy rule: if the query matches the pattern, then perform the
+ * action.
+ */
struct policy {
const struct addrops *ao;
struct sockpat sp[NDIR];
+ struct userpat up;
struct action act;
};
-#define POLICY_INIT(a) { 0, { { { 0 } } }, { a } }
+#define POLICY_INIT(a) { .act.act = a }
+DA_DECL(policy_v, struct policy);
-struct policy_file {
- FILE *fp;
- const struct query *q;
- const char *name;
- const char *what;
- int err;
- int lno;
- struct policy p;
-};
+/* Initialize a policy structure. In this state, it doesn't actually have
+ * any resources allocated (so can be simply discarded) but it's safe to free
+ * (using `free_policy').
+ */
+extern void init_policy(struct policy */*p*/);
-DA_DECL(policy_v, struct policy);
+/* Free a policy structure, resetting it to its freshly-initialized state.
+ * This function is idempotent.
+ */
+extern void free_policy(struct policy */*p*/);
+
+/* Print a policy rule to standard output. */
+extern void print_policy(const struct policy */*p*/);
+
+/* Return true if the query matches the patterns in the policy rule. */
+extern int match_policy(const struct policy */*p*/,
+ const struct query */*q*/);
+
+/*----- Parsing policy files ----------------------------------------------*/
-/*----- Functions provided ------------------------------------------------*/
+/* Possible results from a parse. */
+enum {
+ T_OK, /* Successful: results returned */
+ T_EOL, /* End-of-line found immediately */
+ T_ERROR, /* Some kind of error occurred */
+ T_EOF /* End-of-file found immediately */
+};
+
+/* A context for parsing a policy file. */
+struct policy_file {
+ FILE *fp; /* The file to read from */
+ const struct query *q; /* A query to use for logging */
+ const char *name; /* The name of the file */
+ const char *what; /* A description of the file */
+ int err; /* Have there been any errors? */
+ int lno; /* The current line number */
+ struct policy p; /* Parsed policy rule goes here */
+};
-int sockeq(const struct addrops *ao,
- const struct socket *sa, const struct socket *sb);
-void dputsock(dstr *d, const struct addrops *ao, const struct socket *s);
+/* Open a policy file by NAME. The description WHAT and query Q are used for
+ * formatting error messages for the log.
+ *
+ * This function is somewhat careful only to read from actual regular files,
+ * though (if the filesystem object identified by NAME is a symlink, say) it
+ * might open a device node or other exotic thing without reading it. This
+ * is likely harmless, since we're running as an unprivileged user anyway.
+ */
+extern int open_policy_file(struct policy_file */*pf*/, const char */*name*/,
+ const char */*what*/, const struct query */*q*/,
+ unsigned /*f*/);
+#define OPF_NOENTOK 1u /* Don't complain if file missing */
-void logmsg(const struct query *q, int prio, const char *msg, ...);
+/* Read a policy rule from the file, storing it in PF->p. Return one of the
+ * T_* codes.
+ */
+extern int read_policy_file(struct policy_file */*pf*/);
-void identify(struct query *q);
+/* Close a policy file. It doesn't matter whether the file was completely
+ * read.
+ */
+extern void close_policy_file(struct policy_file */*pf*/);
-void init_policy(struct policy *p);
-void free_policy(struct policy *p);
-void print_policy(const struct policy *p);
-int match_policy(const struct policy *p, const struct query *q);
-int parse_policy(FILE *fp, struct policy *p);
-int open_policy_file(struct policy_file *pf, const char *name,
- const char *what, const struct query *q);
-int read_policy_file(struct policy_file *pf);
-void close_policy_file(struct policy_file *pf);
-int load_policy_file(const char *file, policy_v *pv);
+/* Load a policy file, writing a vector of records into PV. If the policy
+ * file has errors, then leave PV unchanged and return nonzero.
+ */
+extern int load_policy_file(const char */*file*/, policy_v */*pv*/);
/*----- That's all, folks -------------------------------------------------*/