nsssrv_services.c revision 8c3a4809b3420657289b42f028a1c9019b112991
/*
SSSD
Authors:
Stephen Gallagher <sgallagh@redhat.com>
Copyright (C) 2012 Red Hat
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 <http://www.gnu.org/licenses/>.
*/
#include <collection.h>
#include "responder/nss/nsssrv_private.h"
#include "responder/nss/nsssrv_services.h"
#include "responder/common/negcache.h"
#include "db/sysdb_services.h"
struct getserv_ctx {
struct tevent_context *ev;
struct nss_dom_ctx *dctx;
struct sss_domain_info **domains;
char *name;
char *cased_name;
char *proto;
char *cased_proto;
struct ldb_result *res;
};
/* Provider Lookup Logic:
* Iterate through the available caches. If the cached entry is
* present and not expired, return it immediately(*). If it is
* present and expired, add it to a list of domains eligible to
* be checked. If it is in the negative cache, skip over it and
* do not add it to the eligible domain list.
*
* Once we have searched all of the caches, if the entry has not
* been determined to be available, search all domains in order
* to see if any of them contain the requested entry.
*
* (*) Optionally perform a midpoint cache refresh if appropriate.
*/
static struct tevent_req *
struct tevent_context *ev,
const char *service_name,
const char *service_protocol,
struct nss_dom_ctx *dctx)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct getserv_ctx *state;
struct sss_domain_info *dom;
size_t num_domains = 0;
/* Create an array of domains to check. To save resizes, we'll
* assume that all will be checked
*/
struct sss_domain_info *,
num_domains + 1);
goto immediate;
}
/* Store both the case-sensitive and lowercased names
* in the state object, to avoid recalculating the
* lowercase in multiple domains.
*/
if (service_protocol) {
goto immediate;
}
true);
if (!state->cased_proto) {
goto immediate;
}
} else {
}
/* If we're looking up by name */
if (service_name) {
/* Store both the case-sensitive and lowercased names
* in the state object, to avoid recalculating the
* lowercase in multiple domains.
*/
goto immediate;
}
true);
if (!state->cased_name) {
goto immediate;
}
}
while(dom) {
/* if it is a domainless search, skip domains that require fully
* qualified names instead */
}
if (!dom) break;
goto immediate;
}
/* If we're looking up by name */
if (service_name) {
/* Check the negative cache */
dom,
/* If negatively cached, return we didn't find it */
("Service [%s:%s] does not exist in [%s]! "
"(negative cache)\n",
/* If this is a multi-domain search, try the next one */
if (cmdctx->check_next) {
} else {
/* This was a single-domain search.
* exit the loop. Since it was negatively-
* cached, don't add it to the eligible
* domains list.
*/
}
continue;
}
/* Check the cache */
("Checking cache for [%s:%s@%s]\n",
} else { /* Looking up by port */
/* Check the negative cache */
/* If negatively cached, return we didn't find it */
("Service [%lu:%s] does not exist in [%s]! "
"(negative cache)\n",
port,
/* If this is a multi-domain search, try the next one */
if (cmdctx->check_next) {
} else {
/* This was a single-domain search.
* exit the loop. Since it was negatively-
* cached, don't add it to the eligible
* domains list.
*/
}
continue;
}
/* Check the cache */
("Checking cache for [%lu:%s@%s]\n",
port,
}
/* Not found in the cache. Add this domain to the
* list of eligible domains to check the provider.
*/
dom_idx++;
} else {
/* No provider to check. Set the negative cache here */
dom,
/* Failure to set the negative cache is non-fatal.
* We'll log an error and continue.
*/
("Could not set negative cache for [%s][%s]\n",
}
} else {
dom,
/* Failure to set the negative cache is non-fatal.
* We'll log an error and continue.
*/
("Could not set negative cache for [%lu][%s]\n",
}
}
}
/* If this is a multi-domain search, try the next one */
if (cmdctx->check_next) {
} else {
/* This was a single-domain search.
* exit the loop.
*/
}
continue;
}
/* Found a result. Check its validity */
("getservby* returned more than one result!\n"));
goto immediate;
}
SYSDB_LAST_UPDATE, 0);
SYSDB_CACHE_EXPIRE, 0);
midpoint_refresh = 0;
if(nctx->cache_refresh_percent) {
/* If the percentage results in an expiration
* less than ten seconds after the lastUpdate time,
* that's too often we will simply set it to 10s
*/
}
}
if (cacheExpire > now) {
/* cache still valid */
&& midpoint_refresh < now) {
/* We're past the the cache refresh timeout
* We'll return the value from the cache, but we'll also
* queue the cache entry for update out-of-band.
*/
("Performing midpoint cache update\n"));
/* Update the cache */
dom, true,
if (!subreq) {
("Out of memory sending out-of-band data provider "
"request\n"));
/* This is non-fatal, so we'll continue here */
}
/* We don't need to listen for a reply, so we will free the
* request here.
*/
}
/* The cache is valid. Return it */
goto immediate;
} else {
/* Cache is expired. Add this domain to the
* list of eligible domains to check the provider.
*/
dom_idx++;
}
/* If this is a multi-domain search, try the next one */
if (cmdctx->check_next) {
} else {
/* This was a single-domain search.
* exit the loop.
*/
}
}
}
/* No valid cached entries found and
* not found in negative caches.
* Iterate through the domains and try
* to look the data up.
*/
/* No domains to search. Return ENOENT */
goto immediate;
}
return req;
} else {
}
return req;
}
{
struct getserv_ctx *state =
struct tevent_req *subreq;
struct sss_domain_info *dom =
/* Update the cache */
dom,
true,
return EOK;
}
{
char *err_msg;
struct tevent_req *req =
struct getserv_ctx *state =
&err_msg);
("Unable to get information from Data Provider\n"
"dp_error: [%u], errno: [%u], error_msg: [%s]\n"
"Will try to return what we have in cache\n",
}
/* Recheck the cache after the lookup.
* We can ignore the expiration values here, because
* either we have just updated it or the provider is
* offline. Either way, whatever is in the cache should
* be returned, if it exists. Otherwise, move to the
* next provider.
*/
("Critical: Sysdb CTX not found for [%s]!\n",
goto done;
}
if (state->cased_name) {
("Re-checking cache for [%s:%s@%s]\n",
} else {
("Re-checking cache for [%lu:%s@%s]\n",
}
/* Nothing in the cache.
* Set the negative cache
*/
dom,
/* Failure to set the negative cache is non-fatal.
* We'll log an error and continue.
*/
("Could not set negative cache for [%s][%s]\n",
}
} else {
dom,
/* Failure to set the negative cache is non-fatal.
* We'll log an error and continue.
*/
("Could not set negative cache for [%lu][%s]\n",
}
}
/* Need to check other domains */
/* No more domains to search. Return ENOENT */
goto done;
}
/* Set EAGAIN so we will re-enter the mainloop */
}
done:
/* Cache contained results. Return them */
/* An error occurred, fail the request */
}
/* ret == EAGAIN: Reenter mainloop */
return;
}
static errno_t
struct tevent_req *req,
struct ldb_result **_res)
{
struct getserv_ctx *state =
return EOK;
}
static errno_t
struct sss_domain_info *dom,
struct ldb_message **msgs,
unsigned int *count)
{
unsigned int num, i, j;
struct ldb_message *msg;
struct ldb_message_element *el;
const char *orig_name;
char *orig_proto;
struct sized_string cased_name;
struct sized_string cased_proto;
char *tmpstr;
struct sized_string alias;
/* FIXME: Should we account for fully-qualified
* service names?
*/
/* first 2 fields (len and reserved), filled up later */
rsize = 0;
num = 0;
for (i = 0; i < msg_count; i++) {
/* new service */
SYSDB_SVC_CLASS)) {
continue;
}
/* new result starts at end of previous result */
rsize = 0;
/* Get the service name */
("Could not identify service name, skipping\n"));
continue;
}
/* Get the port */
if (!port) {
("No port for service [%s]. Skipping\n"));
}
/* Get the service protocol.
* If more than one is available, select the
* first in the message.
*/
if (el->num_values == 0) {
num = 0;
goto done;
}
("sss_get_cased_name failed, skipping\n"));
continue;
}
+ sizeof(uint32_t)
+ cased_proto.len);
num = 0;
goto done;
}
/* Store the port number */
/* Get the aliases */
if (!el) {
/* No aliases for this user */
num_aliases = 0;
} else {
}
/* Store the alias count */
/* Store the primary name */
&rsize);
/* Store the protocol */
&rsize);
for (j = 0; j < num_aliases; j++) {
num = 0;
goto done;
}
/* Store the alias */
&rsize);
}
num++;
}
done:
/* if num is 0 most probably something went wrong,
* reset packet and return ENOENT */
return ENOENT;
}
return ret;
}
/*****************
* getservbyname *
*****************/
struct sss_names_ctx *names,
char **domain_name,
char **service_name,
char **service_protocol);
static void
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
char *domname;
char *service_name;
char *service_protocol;
struct tevent_req *req;
if (!dctx) {
goto done;
}
/* get service name and protocol */
/* if not terminated fail */
goto done;
}
&domname,
("Could not parse request\n"));
goto done;
}
("Requesting info for service [%s:%s] from [%s]\n",
if (domname) {
goto done;
}
} else {
/* this is a multidomain search */
cmdctx->check_next = true;
}
/* Identify if this backend requires a provider check */
/* Ok, find it! */
dctx);
if (!req) {
goto done;
}
done:
}
struct sss_names_ctx *names,
char **domain_name,
char **service_name,
char **service_protocol)
{
char *rawname;
char *domname;
char *svc_name;
char *protocol;
/* The raw name is at most one character shorter
* than the body length (if the protocol wasn't
* specified). Since this is a common case, we'll
* just assume the maximum memory size for the
* rawname.
*/
if (!rawname) {
goto done;
}
i = j = 0;
/* Copy in the service name */
i++;
j++;
}
if (body[i] != '\0') {
/* blen - 1 was reached without hitting
* a NULL-terminator. No protocol field
* is possible.
*/
goto done;
}
rawname[j] = '\0';
i++;
namelen = i;
j = 0;
/* Copy in the protocol */
if (body[i] == '\0') {
/* Zero-length protocol
* Just set the protocol to NULL
*/
} else {
/* The protocol must be no longer than the remaining
* body space, after the name was copied.
*/
if (!protocol) {
goto done;
}
i++;
j++;
}
if (body[i] != '\0') {
/* blen was reached without hitting
* a NULL-terminator.
*/
goto done;
}
protocol[j] = '\0';
("Body longer than the name and protocol\n"));
goto done;
}
}
("Could not split name and domain of [%s]\n",
rawname));
goto done;
}
done:
return ret;
}
static void
{
unsigned int i;
struct nss_dom_ctx *dctx =
("getservbyname failed\n"));
return;
}
/* Either we succeeded or no domains were eligible */
/* Notify the caller that this entry wasn't found */
} else {
&i);
}
("Could not create response packet: [%s]\n",
}
return;
}
}
struct sss_names_ctx *names,
char **service_protocol)
{
size_t i, j;
char *protocol;
/* Copy in the port */
i = port_and_padding_len;
j = 0;
/* Copy in the protocol */
if (body[i] == '\0') {
/* Zero-length protocol
* Just set the protocol to NULL
*/
} else {
/* The protocol must be no longer than the remaining
* body space.
*/
if (!protocol) {
goto done;
}
i++;
j++;
}
if (body[i] != '\0') {
/* blen was reached without hitting
* a NULL-terminator.
*/
goto done;
}
protocol[j] = '\0';
("Body longer than the name and protocol\n"));
goto done;
}
}
*service_port = port;
done:
return ret;
}
/*****************
* getservbyport *
*****************/
{
struct nss_cmd_ctx *cmdctx;
struct nss_dom_ctx *dctx;
char *service_protocol;
struct tevent_req *req;
if (!dctx) {
goto done;
}
/* get service port and protocol */
/* if not terminated fail */
goto done;
}
&port,
("Could not parse request\n"));
goto done;
}
("Requesting info for service on port [%lu/%s]\n",
/* All port lookups are multidomain searches */
cmdctx->check_next = true;
/* Identify if this backend requires a provider check */
/* Ok, find it! */
if (!req) {
goto done;
}
done:
}
{
return EOK;
}
{
return EOK;
}
{
return EOK;
}