From 66803c6fdb609ed83a78b9194ecb23e9c1b773e7 Mon Sep 17 00:00:00 2001 From: Jose Bollo Date: Wed, 27 Nov 2019 18:31:42 +0100 Subject: Add applications the the localuser family This proposal allows to use separate names for separate applications and separate users running on the loopback interface. Bug-AGL: SPEC-2968 Change-Id: I7c25e89da9f51c10c59a72670339f2ea55b31997 Signed-off-by: Jose Bollo --- README.md | 85 ++++++---- localuser.c | 460 ++++++++++++++++++++++++++++++++++++++----------------- test-localuser.c | 15 ++ 3 files changed, 391 insertions(+), 169 deletions(-) diff --git a/README.md b/README.md index 046c275..b946a7b 100644 --- a/README.md +++ b/README.md @@ -14,50 +14,73 @@ This code is published with the license MIT (see LICENSE.txt) for details. functionality of the GNU C Library (`glibc`) providing host name resolution for *"localuser"* family of virtual hostnames. +It defines the family *"localuser"* of virtual hostnames as one of the +below names: + +- localuser +- localuser-UID +- localuser--APPID +- localuser-UID-APPID +- localuser---APPID + +This can be summarized by the following matrix: + + |------------------|------------------|---------------------|-------------------| + | | **current user** | **user of UID** | **no user** | + |------------------|------------------|---------------------|-------------------| + | **no APP** | localuser | localuser-UID | | + | **app of APPID** | localuser--APPID | localuser-UID-APPID | localuser---APPID | + |------------------|------------------|---------------------|-------------------| + The delivered NSS service defines one virtual host of name `localuser` that resolves to an IP address of the localhost loopback that integrates user ID. -It is intended to enable distinct IP for distinct users. - -The name "localuser" is resolved to the IPv4 address: +It is intended to enable distinct IP for distinct users, distinct application. -``` -127.x.y.z -``` +The name *localuser* family is resolved to the IPv4 address range 127.128.0.0/9 -where x.y.z encode the current user UID in such way that: +The delivered IPv4 address is structured as follow: -``` -UID = 65536*(x - 128) + 256*y + z +```text ++--------+--------+--------+--------+ +:01111111:1abbcccc:dddddeee:ffffffff: ++--------+--------+--------+--------+ ``` -Allowed UID are from 0 to 4194303 included. +When `a` is `1`, the value 11 bits value `bbccccddddd` encodes the APPID +and the 11 bits value `eeedddddddd` encodes the UID. +This is represented by the following hostnames: `localuser--APPID` +and `localuser-UID-APPID`. -And so: +When `abb` is `011`, the 20 bits value `ccccdddddeeeffffffff` encodes the APPID. +This is represented by the following hostnames: `localuser---APPID`. -``` -z = [0..255] -y = [0..255] -x = [128..191] -``` +When `abb` is `010`, the 20 bits value `ccccdddddeeeffffffff` encodes the UID. +This is represented by the following hostnames: `localuser` +and `localuser-UID`. -The names "localuser-${UID}", where UID is a decimal number, are resolved to addresses: +The values `000` and `001` of `abb` are reserved for futur use. -``` -127.x.y.z +Examples: -z = UID % 255 -y = (UID >> 8) % 256 -x = ((UID >> 16) % 256) + 128 -``` +```text +localuser => 127.160.0.0 (when user has UID = 0) +localuser => 127.160.3.233 (when user has UID = 1001) -Examples: +localuser-0 => 127.160.0.0 +localuser-45 => 127.160.0.45 +localuser-1024 => 127.160.4.0 +localuser-1048575 => 127.175.255.255 -``` -localuser => 127.128.0.0 (when user has UID = 0) -localuser => 127.128.3.233 (when user has UID = 1001) -localuser-1024 => 127.128.4.0 (for any user) +localuser---0 => 127.176.0.0 +localuser---45 => 127.176.0.45 +localuser---1048575 => 127.191.255.255 + +localuser-0-0 => 127.192.0.0 +localuser--78 => 127.194.115.233 (when user has UID = 1001) +localuser-23-54 => 127.193.176.23 +localuser-2047-2047 => 127.255.255.255 ``` The service also provides the reverse resolution. @@ -67,7 +90,7 @@ because IPv6 lacks of loopback range. Example: -``` +```text localuser-1024 => ::ffff:127.128.4.0 ``` @@ -78,7 +101,7 @@ For details about NSS integration, see To install this file: -``` +```sh make all && sudo install ``` @@ -88,7 +111,7 @@ script detect-nssdir.sh. If the script detect-nssdir.sh gives the wrong result, just define the variable nssdir when calling make, as below: -``` +```sh make install nssdir=~/lib ``` diff --git a/localuser.c b/localuser.c index f8265e9..ddba031 100644 --- a/localuser.c +++ b/localuser.c @@ -24,28 +24,65 @@ * ----------- * This source file provides NSS (Name Service Switch -see [1]-) facilities * for defining a virtual host of name localuser that resolves to an address - * of the localhost that integrate user ID. - * - * The name "localuser" is resolved to the IPv4 address 127.x.y.z - * where x.y.z resolves to the current user UID = 65536*(x - 128) + 256*y + z - * - * The name "localuser-UID" is resolved to the address 127.x.y.z - * where UID = 65536*(x - 128) + 256*y + z - * - * Allowed UID are from 0 to 4194303 included. + * of the localhost that integrate user ID and/or application ID. * + * It defines the family *"localuser"* of virtual hostnames as one of the + * below names: + * + * - localuser + * - localuser-UID + * - localuser--APPID + * - localuser-UID-APPID + * - localuser---APPID + * + * This can be summarized by the following matrix: + * + * |------------------|------------------|---------------------|-------------------| + * | | **current user** | **user of UID** | **no user** | + * |------------------|------------------|---------------------|-------------------| + * | **no APP** | localuser | localuser-UID | | + * | **app of APPID** | localuser--APPID | localuser-UID-APPID | localuser---APPID | + * |------------------|------------------|---------------------|-------------------| + * + * The delivered NSS service defines one virtual host of name `localuser` + * that resolves to an IP address of the localhost loopback that integrates + * user ID. + * + * It is intended to enable distinct IP for distinct users, distinct application. + * + * The name *localuser* family is resolved to the IPv4 address range 127.128.0.0/9 + * + * The delivered IPv4 address is structured as follow: + * + * ```text + * +--------+--------+--------+--------+ + * :01111111:1abbcccc:dddddeee:ffffffff: + * +--------+--------+--------+--------+ + * ``` + * + * When `a` is `1`, the value 11 bits value `bbccccddddd` encodes the APPID + * and the 11 bits value `eeedddddddd` encodes the UID. + * This is represented by the following hostnames: `localuser--APPID` + * and `localuser-UID-APPID`. + * + * When `abb` is `011`, the 20 bits value `ccccdddddeeeffffffff` encodes the APPID. + * This is represented by the following hostnames: `localuser---APPID`. + * + * When `abb` is `010`, the 20 bits value `ccccdddddeeeffffffff` encodes the UID. + * This is represented by the following hostnames: `localuser` + * and `localuser-UID`. + * + * The values `000` and `001` of `abb` are reserved for futur use. + * * Examples: - * localuser => 127.128.0.0 (when UID = 0) - * localuser => 127.128.3.233 (when UID = 1001) - * localuser-1024 => 127.128.4.0 (always) - * - * This module provides the reverse resolution. - * - * This module provides a value for IPv6: it translate to a IPv4-mapped IPv6 address - * because IPv6 lakes of loopback range. - * - * Example: localuser-1024 => ::ffff:127.128.4.0 - * + * + * ```text + * localuser => 127.128.0.0 (when user has UID = 0) + * localuser => 127.128.3.233 (when user has UID = 1001) + * localuser-1024 => 127.128.4.0 (for any user) + * ``` + * + * The service also provides the reverse resolution. * links * ----- * [1] https://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html @@ -60,98 +97,289 @@ /* string for "localuser" */ static const char localuser[] = "localuser"; static const char separator = '-'; +#define MAXNAMELEN 40 /* defines the length of adresses */ static const int lenip4 = 4; static const int lenip6 = 16; /* masks for IPv4 adresses */ -static const uint32_t prefix_mask = 0xffc00000u; /* 255.192.0.0 */ +static const uint32_t prefix_mask = 0xff800000u; /* 255.128.0.0 */ static const uint32_t prefix_value = 0x7f800000u; /* 127.128.0.0 */ -static const uint32_t locusr_mask = 0x003fffffu; /* 0.63.255.255 */ -/* return the IPv4 localuser address for 'uid' */ -static uint32_t get_localuser(uint32_t uid) -{ - uint32_t adr = (uint32_t)(prefix_value | (locusr_mask & uid)); - return htonl(adr); -} +static const uint32_t locusr_both_ids_mask = 0x7fc00000u; +static const uint32_t locusr_both_ids_prefix = 0x7fc00000u; +static const uint32_t locusr_both_ids_uid_max = 0x000007ffu; +static const uint32_t locusr_both_ids_uid_mask = 0x000007ffu; +static const uint32_t locusr_both_ids_appid_max = 0x000007ffu; +static const uint32_t locusr_both_ids_appid_mask = 0x000007ffu; +static const uint8_t locusr_both_ids_appid_shift = 11; -/* is 'ip' a localuser IPv4 address ? */ -static int is_localuser(uint32_t ip) -{ - return prefix_value == (ntohl(ip) & prefix_mask); -} +static const uint32_t locusr_appid_only_mask = 0x7ff00000u; +static const uint32_t locusr_appid_only_prefix = 0x7fb00000u; +static const uint32_t locusr_appid_only_appid_max = 0x000fffffu; +static const uint32_t locusr_appid_only_appid_mask = 0x000fffffu; -/* return the user of the localuser IPv4 'ip' */ -static uint32_t uid_of_localuser(uint32_t ip) -{ - return (ntohl(ip) & locusr_mask); -} +static const uint32_t locusr_uid_only_mask = 0x7ff00000u; +static const uint32_t locusr_uid_only_prefix = 0x7fa00000u; +static const uint32_t locusr_uid_only_uid_max = 0x000fffffu; +static const uint32_t locusr_uid_only_uid_mask = 0x000fffffu; -/* put in 'buffer' the IPv4 localuser address for 'uid' */ -static void getIPv4(uint32_t *buffer, uint32_t uid) +/* structure for coding/decoding */ +struct lud { - buffer[0] = get_localuser(uid); -} + unsigned has_uid: 1; /* has a uid */ + unsigned has_appid: 1; /* has a appid */ + uint32_t uid; /* uid if any */ + uint32_t appid; /* appid if any */ + uint32_t ipv4; /* IPv4 representation */ + uint32_t len; /* name length */ + char name[MAXNAMELEN]; /* name value */ +}; -/* is 'buffer' pointing a localuser IPv4 address ? */ -static int isIPv4(const uint32_t *buffer) +/* read a 32 bits integer. returns its length in character or -1 on overflow */ +static int read_u32(const char *str, uint32_t *val) { - return is_localuser(buffer[0]); + char c; + int p; + uint32_t a, b; + + a = 0; + c = str[p = 0]; + while ('0' <= c && c <= '9') { + b = (a << 3) + (a << 1) + (uint32_t)(c - '0'); + if (b < a) + return -1; /* overflow */ + a = b; + c = str[++p]; + } + *val = a; + return p; } -/* return the user of the localuser IPv4 pointed by 'buffer' */ -static uint32_t uidIPv4(const uint32_t *buffer) +/* write a 32 bits integer and return the count of char writen */ +static unsigned write_u32(char *str, uint32_t val) { - return uid_of_localuser(buffer[0]); + unsigned w, l, u; + char c; + + l = w = 0; + while (val > 9) { + str[w++] = (char)('0' + val % 10); + val /= 10; + } + str[w++] = (char)('0' + val); + u = w; + while (--u > l) { + c = str[u]; + str[u] = str[l]; + str[l++] = c; + } + return w; } -/* put in 'buffer' the IPv6 localuser address for 'uid' */ -static void getIPv6(uint32_t *buffer, uint32_t uid) +static void encode_name(struct lud *lud) { - buffer[0] = 0; - buffer[1] = 0; - buffer[2] = htonl(0xffff); - buffer[3] = get_localuser(uid); + unsigned i; + + /* encode "localuser-" */ + i = (int)(sizeof localuser - 1); + memcpy(lud->name, localuser, i); + + /* encode the UID if needed */ + if (!lud->has_uid) { + lud->name[i++] = separator; + lud->name[i++] = separator; + } else if (lud->uid != (uint32_t)getuid()) { + lud->name[i++] = separator; + i += write_u32(&lud->name[i], lud->uid); + } else if (lud->has_appid) + lud->name[i++] = separator; + + /* encode the APPID if needed */ + if (lud->has_appid) { + lud->name[i++] = separator; + i += write_u32(&lud->name[i], lud->appid); + } + + /* finish */ + lud->len = i; + lud->name[i] = 0; } -/* is 'buffer' pointing a localuser IPv6 address ? */ -static int isIPv6(const uint32_t *buffer) +/* + * Decode the name if valid and stores its ip in lud + * Returns: + * - 0: not a localuser name + * - 1: valid local user name + * - -1: invalid localuser name + * - -2: out of range localuser name + */ +static int decode_name(const char *name, struct lud *lud) { - return buffer[0] == 0 && buffer[1] == 0 - && buffer[2] == htonl(0xffff) && is_localuser(buffer[3]); + int i, r; + uint32_t adr; + + /* test the prefix of the name */ + i = (int)(sizeof localuser - 1); + if (strncmp(name, localuser, (size_t)i) != 0) + return 0; + + /* prefix matches "localuser" */ + if (!name[i]) { + /* terminated string: "localuser" */ + lud->has_uid = 1; + lud->uid = (uint32_t)getuid(); /* use current UID */ + lud->has_appid = 0; + } else { + /* should be "localuser-..." */ + if (name[i] != separator) + return -1; + /* found "localuser-..." */ + if (name[++i] == separator) { + /* found "localuser--..." */ + if (name[++i] == separator) { + /* found "localuser---..." */ + ++i; + lud->has_uid = 0; + } else { + /* found "localuser--x.." */ + lud->uid = (uint32_t)getuid(); /* use current UID */ + lud->has_uid = 1; + } + lud->has_appid = 1; + } else { + /* found "localuser-X..." with X not being a dash */ + r = read_u32(&name[i], &lud->uid); + if (r <= 0) + return -1; + /* found "localuser-UID..." */ + i += r; + lud->has_uid = 1; + if (name[i] != separator) + lud->has_appid = 0; + else { + /* found "localuser-UID-..." */ + i++; + lud->has_appid = 1; + } + } + /* look if appid must be read */ + if (lud->has_appid) { + /* found "localuser-[UID|-]-..." */ + r = read_u32(&name[i], &lud->appid); + if (r <= 0) + return -1; + /* found "localuser-[UID|-]-APPID..." */ + i += r; + } + /* the name should be finished now */ + if (name[i]) + return -1; + } + + /* encode the address */ + if (lud->has_appid && lud->has_uid) { + /* case of UID and APPID */ + if (lud->appid > locusr_both_ids_appid_max) + return -2; + if (lud->uid > locusr_both_ids_uid_max) + return -2; + adr = (uint32_t)(locusr_both_ids_prefix + | (lud->appid << locusr_both_ids_appid_shift) + | lud->uid); + } else if (lud->has_appid) { + /* case of only APPID */ + if (lud->appid > locusr_appid_only_appid_max) + return -2; + adr = (uint32_t)(locusr_appid_only_prefix | lud->appid); + } else { + /* case of only UID */ + if (lud->uid > locusr_uid_only_uid_max) + return -2; + adr = (uint32_t)(locusr_uid_only_prefix | lud->uid); + } + lud->ipv4 = htonl(adr); + + encode_name(lud); + return 1; } -/* return the user of the localuser IPv6 pointed by 'buffer' */ -static uint32_t uidIPv6(const uint32_t *buffer) +/* + * Decode the ipv4 if valid and stores its data in lud + * Returns: + * - 0: not a localuser ip + * - 1: valid local user ip + * - -1: invalid localuser ip + */ +static int decode_ipv4(uint32_t ipv4, struct lud *lud) { - return uid_of_localuser(buffer[3]); + uint32_t adr; + + /* check the address range */ + adr = ntohl(ipv4); + if ((adr & prefix_mask) != prefix_value) + return 0; + + /* decode */ + lud->ipv4 = ipv4; + if ((adr & locusr_both_ids_mask) == locusr_both_ids_prefix) { + lud->has_uid = 1; + lud->has_appid = 1; + lud->uid = adr & locusr_both_ids_uid_mask; + if (lud->uid > locusr_both_ids_uid_max) + return -1; + lud->appid = (adr >> locusr_both_ids_appid_shift) & locusr_both_ids_appid_mask; + if (lud->appid > locusr_both_ids_appid_max) + return -1; + } else if ((adr & locusr_appid_only_mask) == locusr_appid_only_prefix) { + lud->has_uid = 0; + lud->has_appid = 1; + lud->appid = adr & locusr_appid_only_appid_mask; + if (lud->appid > locusr_appid_only_appid_max) + return -1; + } else if ((adr & locusr_uid_only_mask) == locusr_uid_only_prefix) { + lud->has_uid = 1; + lud->has_appid = 0; + lud->uid = adr & locusr_uid_only_uid_mask; + if (lud->uid > locusr_uid_only_uid_max) + return -1; + } else { + /* reserved address */ + return -1; + } + + encode_name(lud); + return 1; } /* fill the output entry */ static enum nss_status fillent( - const char *name, + struct lud *lud, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, - int *h_errnop, - uint32_t uid) + int *h_errnop) { - int alen = 1 + (int)strlen(name); - int len = af == AF_INET ? lenip4 : lenip6; + uint32_t *bufip; + int len, alen; /* check the family */ - if (af != AF_INET && af != AF_INET6) { + if (af == AF_INET) + len = lenip4; + else if (af == AF_INET6) + len = lenip6; + else { *errnop = EINVAL; *h_errnop = NO_RECOVERY; return NSS_STATUS_UNAVAIL; } /* fill aliases and addr_list */ - if (buflen < 2 * sizeof result->h_aliases[0] + alen + len) { + alen = 1 + lud->len; + if (buflen < (2 * sizeof result->h_aliases[0]) + alen + len) { *errnop = ERANGE; *h_errnop = NO_RECOVERY; return NSS_STATUS_TRYAGAIN; @@ -163,10 +391,16 @@ static enum nss_status fillent( result->h_addr_list = (char**)buffer; result->h_addr_list[0] = (char*)&result->h_addr_list[2]; result->h_name = &result->h_addr_list[0][len]; + memcpy(result->h_name, lud->name, alen); result->h_aliases = &result->h_addr_list[1]; result->h_addr_list[1] = NULL; - (af == AF_INET ? getIPv4 : getIPv6)((uint32_t*)result->h_addr_list[0], uid); - memcpy(result->h_name, name, alen); + bufip = (uint32_t*)result->h_addr_list[0]; + if (af == AF_INET6) { + *bufip++ = 0; + *bufip++ = 0; + *bufip++ = htonl(0xffff); + } + *bufip = lud->ipv4; return NSS_STATUS_SUCCESS; } @@ -181,36 +415,10 @@ enum nss_status _nss_localuser_gethostbyname2_r( int *errnop, int *h_errnop) { - int valid; - uint32_t uid; - const char *i; - char c; + struct lud lud; - /* test the name */ - valid = !strncmp(name, localuser, sizeof localuser - 1); - if (valid) { - c = name[sizeof localuser - 1]; - if (!c) { - /* terminated string: use current UID */ - uid = (uint32_t)getuid(); - } else if (c != separator) { - valid = 0; - } else { - /* has a uid specification */ - i = &name[sizeof localuser]; - c = *i; - valid = '0' <= c && c <= '9'; - if (valid) { - uid = (uint32_t)(c - '0'); - while ((c = *++i) && (valid = '0' <= c && c <= '9')) { - uid = (uid << 3) + (uid << 1) + (uint32_t)(c - '0'); - } - if (valid) - valid = uid == (uid & locusr_mask); - } - } - } - if (!valid) { + /* decode the name */ + if (decode_name(name, &lud) <= 0) { *h_errnop = HOST_NOT_FOUND; return NSS_STATUS_NOTFOUND; } @@ -220,7 +428,7 @@ enum nss_status _nss_localuser_gethostbyname2_r( af = AF_INET; /* fill the result */ - return fillent(name, af, result, buffer, buflen, errnop, h_errnop, uid); + return fillent(&lud, af, result, buffer, buflen, errnop, h_errnop); } /* use gethostbyname2 implementation */ @@ -250,9 +458,9 @@ enum nss_status _nss_localuser_gethostbyaddr_r( int *errnop, int *h_errnop) { - char c, name[40 + sizeof localuser]; - uint32_t uid, x; - int l, u; + struct lud lud; + const uint32_t *bufip = (const uint32_t*)addr; + int check; /* set default family */ if (af == AF_UNSPEC) { @@ -262,40 +470,16 @@ enum nss_status _nss_localuser_gethostbyaddr_r( af = AF_INET6; } - /* check whether the IP comforms to localuser */ - if (af == AF_INET && len == lenip4 && isIPv4((const uint32_t*)addr)) { - /* yes, it's a IPv4, get the uid */ - uid = uidIPv4((const uint32_t*)addr); - } else if (af == AF_INET6 && len == lenip6 && isIPv6((const uint32_t*)addr)) { - /* yes, it's a IPv6, get the uid */ - uid = uidIPv6((const uint32_t*)addr); - } else { - /* no */ - /* fail */ - *errnop = EINVAL; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_NOTFOUND; - } + /* pre process of ipv6 */ + if (af == AF_INET6 && len == lenip6) + check = (*bufip++ == 0 && *bufip++ == 0 && *bufip++ == htonl(0xffff)); + else + check = (af == AF_INET && len == lenip4); - /* build the name */ - memcpy(name, localuser, sizeof localuser - 1); - if (uid == (uint32_t)getuid()) - name[sizeof localuser - 1] = 0; - else { - x = uid; - name[sizeof localuser - 1] = separator; - l = u = (int)sizeof localuser; - do { - name[u++] = (char)('0' + x % 10); - x /= 10; - } while(x); - name[u--] = 0; - while (u > l) { - c = name[u]; - name[u--] = name[l]; - name[l++] = c; - } - } - /* fill the result */ - return fillent(name, af, result, buffer, buflen, errnop, h_errnop, uid); + if (check && decode_ipv4(*bufip, &lud) == 1) + return fillent(&lud, af, result, buffer, buflen, errnop, h_errnop); + + *errnop = EINVAL; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_NOTFOUND; } diff --git a/test-localuser.c b/test-localuser.c index ed27138..7029fd9 100644 --- a/test-localuser.c +++ b/test-localuser.c @@ -20,6 +20,7 @@ * THE SOFTWARE. */ #include +#include #include void dumphostent(char *tag, char *arg, struct hostent *h) @@ -33,6 +34,12 @@ void dumphostent(char *tag, char *arg, struct hostent *h) (int)(unsigned char)h->h_addr_list[0][1], (int)(unsigned char)h->h_addr_list[0][2], (int)(unsigned char)h->h_addr_list[0][3]); + else if (h->h_addrtype == AF_INET6) + printf("ipv6: %08lx.%08lx.%08lx.%08lx\n", + (long)ntohl(((uint32_t*)h->h_addr_list[0])[0]), + (long)ntohl(((uint32_t*)h->h_addr_list[0])[1]), + (long)ntohl(((uint32_t*)h->h_addr_list[0])[2]), + (long)ntohl(((uint32_t*)h->h_addr_list[0])[3])); } else { printf("NULL!\n"); } @@ -51,6 +58,14 @@ int main(int ac, char **av) h = gethostbyaddr(h->h_addr_list[0], h->h_length, h->h_addrtype); dumphostent("addr->name", *av, h); } + + h = gethostbyname2(*av, AF_INET6); + dumphostent("name->addr", *av, h); + + if (h) { + h = gethostbyaddr(h->h_addr_list[0], h->h_length, h->h_addrtype); + dumphostent("addr->name", *av, h); + } } return 0; } -- cgit 1.2.3-korg