X-Git-Url: https://git.distorted.org.uk/~mdw/yaid/blobdiff_plain/3b1bed1dc06df72338b0ff180169525681c114d0..HEAD:/yaid.h diff --git a/yaid.h b/yaid.h index f8f6470..eb874cb 100644 --- a/yaid.h +++ b/yaid.h @@ -43,87 +43,125 @@ #include #include #include +#include #include +#include #include #include +#include #include #include #include #include +#include #include #include #include +#include #include #include #include #include +#include +#include #include #include #include #include +#include -/*----- System specifics --------------------------------------------------*/ +/*----- Address family handling -------------------------------------------*/ -#define SYS_UNDEF 0 -#define SYS_LINUX 1 - -#if SYS == SYS_LINUX -# include -# include -#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) @@ -131,25 +169,53 @@ enum { 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, @@ -158,35 +224,57 @@ enum { 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") \ @@ -202,53 +290,98 @@ enum { 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 -------------------------------------------------*/