/* * Copyright 2018 IoT.bzh * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* * localuser.c * ----------- * 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. * * 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 * * links * ----- * [1] https://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html */ #include #include #include #include #include #include /* string for "localuser" */ static const char localuser[] = "localuser"; static const char separator = '-'; /* 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_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); } /* is 'ip' a localuser IPv4 address ? */ static int is_localuser(uint32_t ip) { return prefix_value == (ntohl(ip) & prefix_mask); } /* return the user of the localuser IPv4 'ip' */ static uint32_t uid_of_localuser(uint32_t ip) { return (ntohl(ip) & locusr_mask); } /* put in 'buffer' the IPv4 localuser address for 'uid' */ static void getIPv4(uint32_t *buffer, uint32_t uid) { buffer[0] = get_localuser(uid); } /* is 'buffer' pointing a localuser IPv4 address ? */ static int isIPv4(const uint32_t *buffer) { return is_localuser(buffer[0]); } /* return the user of the localuser IPv4 pointed by 'buffer' */ static uint32_t uidIPv4(const uint32_t *buffer) { return uid_of_localuser(buffer[0]); } /* put in 'buffer' the IPv6 localuser address for 'uid' */ static void getIPv6(uint32_t *buffer, uint32_t uid) { buffer[0] = 0; buffer[1] = 0; buffer[2] = htonl(0xffff); buffer[3] = get_localuser(uid); } /* is 'buffer' pointing a localuser IPv6 address ? */ static int isIPv6(const uint32_t *buffer) { return buffer[0] == 0 && buffer[1] == 0 && buffer[2] == htonl(0xffff) && is_localuser(buffer[3]); } /* return the user of the localuser IPv6 pointed by 'buffer' */ static uint32_t uidIPv6(const uint32_t *buffer) { return uid_of_localuser(buffer[3]); } /* fill the output entry */ static enum nss_status fillent( const char *name, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop, uint32_t uid) { int alen = 1 + (int)strlen(name); int len = af == AF_INET ? lenip4 : lenip6; /* check the family */ if (af != AF_INET && af != AF_INET6) { *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) { *errnop = ERANGE; *h_errnop = NO_RECOVERY; return NSS_STATUS_TRYAGAIN; } /* fill the result */ result->h_addrtype = af; result->h_length = len; 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]; 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); return NSS_STATUS_SUCCESS; } /* gethostbyname2 implementation for NSS */ enum nss_status _nss_localuser_gethostbyname2_r( const char *name, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop) { int valid; uint32_t uid; const char *i; char c; /* 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) { *h_errnop = HOST_NOT_FOUND; return NSS_STATUS_NOTFOUND; } /* set default family to IPv4 */ if (af == AF_UNSPEC) af = AF_INET; /* fill the result */ return fillent(name, af, result, buffer, buflen, errnop, h_errnop, uid); } /* use gethostbyname2 implementation */ enum nss_status _nss_localuser_gethostbyname_r( const char *name, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop) { return _nss_localuser_gethostbyname2_r(name, AF_UNSPEC, result, buffer, buflen, errnop, h_errnop); } /* get the name of the address */ enum nss_status _nss_localuser_gethostbyaddr_r( const void *addr, int len, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop) { char c, name[40 + sizeof localuser]; uint32_t uid, x; int l, u; /* set default family */ if (af == AF_UNSPEC) { if (len == lenip4) af = AF_INET; else if (len == lenip6) 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; } /* 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); }