X-Git-Url: https://git.distorted.org.uk/~mdw/yaid/blobdiff_plain/3bfd21ff72dfd7f04c786e49de9c13998d1ed8d4..c3794524deb80d18c5b3be72a7572b34fad409a1:/yaid.h diff --git a/yaid.h b/yaid.h index 80aa98b..126c201 100644 --- a/yaid.h +++ b/yaid.h @@ -67,63 +67,93 @@ #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 +161,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)) +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,18 +216,16 @@ 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 @@ -180,13 +236,28 @@ struct query { } 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 logmsg(const struct query */*q*/, + int /*prio*/, 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*/); + +/* 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,54 +273,85 @@ 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 policy rule: if the query matches the pattern, then perform the + * action. + */ struct policy { const struct addrops *ao; struct sockpat sp[NDIR]; 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_EOF, /* End-of-file found immediately */ + T_ERROR /* Some kind of error occurred */ +}; -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); +/* 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 */ +}; -void logmsg(const struct query *q, int prio, const char *msg, ...); +/* Open a policy file by NAME. The description WHAT and query Q are used for + * formatting error messages for the log. + */ +extern int open_policy_file(struct policy_file */*pf*/, const char */*name*/, + const char */*what*/, const struct query */*q*/); -void identify(struct query *q); -void init_sys(void); +/* 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*/); + +/* 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 -------------------------------------------------*/