From 9c96d570e1562fb6a79ffbe8f23f30c68efca9cc Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Apr 23 2020 11:40:43 +0000 Subject: NSS: Add client support for networks (non-enumeration) Signed-off-by: Samuel Cabrero Reviewed-by: Pavel Březina --- diff --git a/Makefile.am b/Makefile.am index 77a653b..45e0cf6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4106,6 +4106,7 @@ libnss_sss_la_SOURCES = \ src/sss_client/nss_netgroup.c \ src/sss_client/nss_services.c \ src/sss_client/nss_hosts.c \ + src/sss_client/nss_ipnetworks.c \ src/sss_client/sss_cli.h \ src/sss_client/nss_compat.h \ src/sss_client/nss_common.h \ diff --git a/src/sss_client/nss_ipnetworks.c b/src/sss_client/nss_ipnetworks.c new file mode 100644 index 0000000..7a475b7 --- /dev/null +++ b/src/sss_client/nss_ipnetworks.c @@ -0,0 +1,386 @@ +/* + SSSD + + Authors: + Samuel Cabrero + + Copyright (C) 2020 SUSE LINUX GmbH, Nuernberg, Germany. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sss_cli.h" + +/* GETNETBYNAME Request + * + * 0-X: One zero-terminated string (name) + * + * GETNETBYADDR Request: + * 0-3: 32-bit unsigned address family + * 4-7: 32-bit unsigned address length + * 8-X: binary address + * + * Replies: + * 0-3: 32-bit unsigned number of results + * 4-7: 32-bit unsigned (reserved/padding) + * 7-X: Result data (blocks equal to number of results) + * + * Result data: + * 0-3: 32-bit unsigned number of aliases + * 4-X: sequence of zero-terminated strings + * (name, address, zero or more aliases) + */ + +struct sss_nss_net_rep { + struct netent *result; + char *buffer; + size_t buflen; +}; + +#define IP_NETWORK_METADATA_COUNT 8 + +static errno_t +sss_nss_net_readrep(struct sss_nss_net_rep *sr, + uint8_t *buf, size_t *len, int type) +{ + errno_t ret; + char *net_addrstr; + uint32_t net_addr; + uint32_t num_aliases; + const char *sbuf; + size_t i, l, slen, dlen, pad, ptmem, alen; + + /* Only AF_INET is supported */ + if (type != AF_INET) { + return EBADMSG; + } + + /* Buffer must contain one 32-bit integer, + * at least one character and null terminator + * for the name, at least one character and a + * null terminator for the address and a null + * terminator for the aliases. + */ + if (*len < 9) { + /* not enough data, bad packet */ + return EBADMSG; + } + + /* Get the number of aliases */ + SAFEALIGN_COPY_UINT32(&num_aliases, buf, NULL); + + sbuf = (char *)&buf[sizeof(uint32_t)]; + slen = *len - (sizeof(uint32_t)); + dlen = sr->buflen; + i = 0; + + /* Copy the name */ + sr->result->n_name = &(sr->buffer[i]); + ret = sss_readrep_copy_string(sbuf, &i, + &slen, &dlen, + &sr->result->n_name, + NULL); + if (ret != EOK) { + return ret; + } + + /* Copy the address */ + net_addrstr = &(sr->buffer[i]); + ret = sss_readrep_copy_string(sbuf, &i, + &slen, &dlen, + &net_addrstr, + NULL); + if (ret != EOK) { + return ret; + } + + if (inet_pton(AF_INET, net_addrstr, &net_addr)) { + sr->result->n_addrtype = AF_INET; + } else { + /* Skip illegal address */ + return EAFNOSUPPORT; + } + + /* result->n_net must be represented in host byte order */ + sr->result->n_net = ntohl(net_addr); + + /* Copy the aliases */ + pad = PADDING_SIZE(i, char *); + sr->result->n_aliases = DISCARD_ALIGN(&(sr->buffer[i+pad]), char **); + + ptmem = (sizeof(char *) * (num_aliases + 1)) + pad; + if (ptmem > dlen) { + /* Not ENOMEM, ERANGE is what glibc looks for */ + return ERANGE; + } + + dlen -= ptmem; + ptmem += i; + + /* Terminate array */ + sr->result->n_aliases[num_aliases] = NULL; + + for (l = 0; l < num_aliases; l++) { + sr->result->n_aliases[l] = &(sr->buffer[ptmem]); + ret = sss_readrep_copy_string(sbuf, &i, + &slen, &dlen, + &sr->result->n_aliases[l], + &alen); + if (ret != EOK) { + return ret; + } + + ptmem += alen + 1; + } + + *len = slen - i; + + return EOK; +} + +enum nss_status +_nss_sss_getnetbyname_r(const char *name, + struct netent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_net_rep netrep; + size_t name_len; + uint8_t *repbuf; + size_t replen, len; + uint32_t num_results; + enum nss_status nret; + int ret; + + /* Caught once glibc passing in buffer == 0x0 */ + if (buffer == NULL || buflen == 0) { + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + ret = sss_strnlen(name, SSS_NAME_MAX, &name_len); + if (ret != 0) { + *errnop = EINVAL; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_UNAVAIL; + } + + rd.len = name_len + 1; + rd.data = name; + + sss_nss_lock(); + + nret = sss_nss_make_request(SSS_NSS_GETNETBYNAME, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + *h_errnop = NETDB_INTERNAL; + goto out; + } + + netrep.result = result; + netrep.buffer = buffer; + netrep.buflen = buflen; + + /* Get number of results from repbuf. */ + SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL); + + /* No results if not found */ + if (num_results == 0) { + free(repbuf); + nret = NSS_STATUS_NOTFOUND; + *h_errnop = HOST_NOT_FOUND; + goto out; + } + + /* Only 1 result is accepted for this function */ + if (num_results != 1) { + free(repbuf); + *errnop = EBADMSG; + *h_errnop = NETDB_INTERNAL; + nret = NSS_STATUS_TRYAGAIN; + goto out; + } + + len = replen - IP_NETWORK_METADATA_COUNT; + ret = sss_nss_net_readrep(&netrep, repbuf + IP_NETWORK_METADATA_COUNT, + &len, AF_INET); + free(repbuf); + + /* If network name is valid but does not have an IP address of the + * requested address family return the correct error + */ + if (ret == EAFNOSUPPORT) { + *h_errnop = NO_DATA; + nret = NSS_STATUS_TRYAGAIN; + goto out; + } else if (ret) { + *errnop = ret; + nret = NSS_STATUS_TRYAGAIN; + *h_errnop = NETDB_INTERNAL; + goto out; + } + + nret = NSS_STATUS_SUCCESS; + +out: + sss_nss_unlock(); + + return nret; +} + +enum nss_status +_nss_sss_getnetbyaddr_r(uint32_t addr, int type, + struct netent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_net_rep netrep; + uint8_t *repbuf; + uint8_t *data; + size_t replen, len; + uint32_t num_results; + enum nss_status nret; + int ret; + size_t data_len = 0; + size_t ctr = 0; + socklen_t addrlen; + + if (type != AF_INET) { + *errnop = EAFNOSUPPORT; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_UNAVAIL; + } + + /* Caught once glibc passing in buffer == 0x0 */ + if (buffer == NULL || buflen == 0) { + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + addrlen = INADDRSZ; + + data_len = sizeof(uint32_t) + sizeof(socklen_t) + addrlen; + data = malloc(data_len); + if (data == NULL) { + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + /* Push type */ + SAFEALIGN_SETMEM_VALUE(data, type, int, &ctr); + + /* Push LEN */ + SAFEALIGN_SETMEM_VALUE(data + ctr, addrlen, socklen_t, &ctr); + + /* Push ADDR */ + SAFEALIGN_SETMEM_STRING(data + ctr, &addr, addrlen, &ctr); + + rd.data = data; + rd.len = data_len; + + sss_nss_lock(); + + nret = sss_nss_make_request(SSS_NSS_GETNETBYADDR, &rd, + &repbuf, &replen, errnop); + free(data); + if (nret != NSS_STATUS_SUCCESS) { + *h_errnop = NETDB_INTERNAL; + goto out; + } + + netrep.result = result; + netrep.buffer = buffer; + netrep.buflen = buflen; + + /* Get number of results from repbuf. */ + SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL); + + /* No results if not found */ + if (num_results == 0) { + free(repbuf); + nret = NSS_STATUS_NOTFOUND; + *h_errnop = HOST_NOT_FOUND; + goto out; + } + + /* Only 1 result is accepted for this function */ + if (num_results != 1) { + free(repbuf); + *errnop = EBADMSG; + *h_errnop = NETDB_INTERNAL; + nret = NSS_STATUS_TRYAGAIN; + goto out; + } + + len = replen - IP_NETWORK_METADATA_COUNT; + ret = sss_nss_net_readrep(&netrep, repbuf + IP_NETWORK_METADATA_COUNT, + &len, type); + free(repbuf); + + /* If network name is valid but does not have an IP address of the + * requested address family return the correct error + */ + if (ret == EAFNOSUPPORT) { + *h_errnop = NO_DATA; + nret = NSS_STATUS_TRYAGAIN; + goto out; + } else if (ret) { + *errnop = ret; + nret = NSS_STATUS_TRYAGAIN; + *h_errnop = NETDB_INTERNAL; + goto out; + } + + nret = NSS_STATUS_SUCCESS; + +out: + sss_nss_unlock(); + + return nret; +} + +enum nss_status +_nss_sss_setnetent(void) +{ + return NSS_STATUS_UNAVAIL; +} + +enum nss_status +_nss_sss_getnetent_r(struct netent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop) +{ + return NSS_STATUS_UNAVAIL; +} + +enum nss_status +_nss_sss_endnetent(void) +{ + return NSS_STATUS_UNAVAIL; +} diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index f636749..6f588de 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -128,7 +128,7 @@ enum sss_cli_command { SSS_NSS_GETNETGRENT = 0x0062, SSS_NSS_ENDNETGRENT = 0x0063, /* SSS_NSS_INNETGR = 0x0064, */ -#if 0 + /* networks */ SSS_NSS_GETNETBYNAME = 0x0071, @@ -137,6 +137,7 @@ enum sss_cli_command { SSS_NSS_GETNETENT = 0x0074, SSS_NSS_ENDNETENT = 0x0075, +#if 0 /* protocols */ SSS_NSS_GETPROTOBYNAME = 0x0081, diff --git a/src/sss_client/sss_nss.exports b/src/sss_client/sss_nss.exports index ed6d30a..d833ddf 100644 --- a/src/sss_client/sss_nss.exports +++ b/src/sss_client/sss_nss.exports @@ -38,11 +38,11 @@ EXPORTED { _nss_sss_getnetgrent_r; _nss_sss_endnetgrent; - #_nss_sss_getnetbyname_r; - #_nss_sss_getnetbyaddr_r; - #_nss_sss_setnetent; - #_nss_sss_getnetent_r; - #_nss_sss_endnetent; + _nss_sss_getnetbyname_r; + _nss_sss_getnetbyaddr_r; + _nss_sss_setnetent; + _nss_sss_getnetent_r; + _nss_sss_endnetent; #_nss_sss_getprotobyname_r; #_nss_sss_getprotobynumber_r;