* The backend socket has now support of credentials of peer clients

* Added: CLICON_NACM_CREDENTIALS and CLICON_NACM_RECOVERY_USER
This commit is contained in:
Olof hagsand 2019-10-18 19:33:23 +02:00
parent 77b491c568
commit 279614d64f
33 changed files with 951 additions and 145 deletions

View file

@ -145,7 +145,6 @@ backend_client_rm(clicon_handle h,
}
ce_prev = &c->ce_next;
}
return backend_client_delete(h, ce); /* actually purge it */
}
@ -1240,6 +1239,68 @@ from_client_ping(clicon_handle h,
return 0;
}
/*! Verify nacm user with peer uid credentials
* @param[in] mode Peer credential mode: none, exact or except
* @param[in] peername Peer username if any
* @param[in] username username received in XML (eg for NACM)
* @param[out] cbret Set with netconf error message if ret == 0
* @retval -1 Error
* @retval 0 Not verified (cbret set)
* @retval 1 Verified
* Credentials OK if both NACM user and peer useri s defined AND one of the
* following is true:
* - peer user is same as NACM user
* - peer user is root (can be any NACM user)
* - peer user is www (can be any NACM user)
*/
static int
verify_nacm_user(enum nacm_credentials_t mode,
char *peername,
char *nacmname,
cbuf *cbret)
{
int retval = -1;
cbuf *cbmsg = NULL;
if (mode == NC_NONE)
return 1;
if (peername == NULL){
if (netconf_access_denied(cbret, "application", "No peer user credentials available") < 0)
goto done;
goto fail;
}
if (nacmname == NULL){
if (netconf_access_denied(cbret, "application", "No NACM available") < 0)
goto done;
goto fail;
}
if (mode == NC_EXCEPT){
if (strcmp(peername, "root") == 0)
goto ok;
if (strcmp(peername, WWWUSER) == 0)
goto ok;
}
if (strcmp(peername, nacmname) != 0){
if ((cbmsg = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cbmsg, "User %s credential not matching NACM user %s", peername, nacmname);
if (netconf_access_denied(cbret, "application", cbuf_get(cbmsg)) < 0)
goto done;
goto fail;
}
ok:
retval = 1;
done:
if (cbmsg)
cbuf_free(cbmsg);
return retval;
fail:
retval = 0;
goto done;
}
/*! An internal clicon message has arrived from a client. Receive and dispatch.
* @param[in] h Clicon handle
* @param[in] s Socket where message arrived. read from this.
@ -1322,6 +1383,12 @@ from_client_msg(clicon_handle h,
if ((ret = nacm_access_pre(h, username, NACM_RPC, &xnacm)) < 0)
goto done;
if (ret == 0){ /* Do NACM validation */
enum nacm_credentials_t mode;
mode = clicon_nacm_credentials(h);
if ((ret = verify_nacm_user(mode, ce->ce_username, username, cbret)) < 0)
goto done;
if (ret == 0) /* credentials fail */
goto reply;
/* NACM rpc operation exec validation */
if ((ret = nacm_rpc(rpc, module, username, xnacm, cbret)) < 0)
goto done;

View file

@ -43,15 +43,15 @@
* Keep state about every connected client.
*/
struct client_entry{
struct client_entry *ce_next; /* The clients linked list */
struct sockaddr ce_addr; /* The clients (UNIX domain) address */
int ce_s; /* stream socket to client */
int ce_nr; /* Client number (for dbg/tracing) */
int ce_stat_in; /* Nr of received msgs from client */
int ce_stat_out;/* Nr of sent msgs to client */
int ce_pid; /* Process id */
int ce_uid; /* User id of calling process */
clicon_handle ce_handle; /* clicon config handle (all clients have same?) */
struct client_entry *ce_next; /* The clients linked list */
struct sockaddr ce_addr; /* The clients (UNIX domain) address */
int ce_s; /* stream socket to client */
int ce_nr; /* Client number (for dbg/tracing) */
int ce_stat_in; /* Nr of received msgs from client */
int ce_stat_out;/* Nr of sent msgs to client */
int ce_pid; /* Peer Process id */
char *ce_username;/* Translated from peer user cred */
clicon_handle ce_handle; /* clicon config handle (all clients have same?) */
};

View file

@ -51,13 +51,12 @@
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/param.h>
#ifdef HAVE_SYS_UCRED_H
#include <sys/types.h>
#include <sys/ucred.h>
#endif
#define __USE_GNU /* for ucred */
#define _GNU_SOURCE /* for ucred */
#include <sys/socket.h>
#ifdef HAVE_LOCAL_PEERCRED
#include <sys/ucred.h>
#endif
#include <sys/param.h>
#include <sys/types.h>
@ -226,63 +225,65 @@ backend_accept_client(int fd,
int retval = -1;
clicon_handle h = (clicon_handle)arg;
int s;
struct sockaddr_un from;
struct sockaddr from = {0,};
socklen_t len;
struct client_entry *ce;
#ifdef DONT_WORK /* XXX HAVE_SYS_UCRED_H */
struct xucred credentials; /* FreeBSD. */
socklen_t clen;
#elif defined(SO_PEERCRED)
struct ucred credentials; /* Linux. */
socklen_t clen;
#endif
char *config_group;
struct group *gr;
char *mem;
int i;
char *name = NULL;
#ifdef HAVE_SO_PEERCRED
socklen_t clen;
struct ucred cr = {0,}; /* Linux. */
#elif defined(HAVE_GETPEEREID)
uid_t euid;
uid_t guid;
#endif
clicon_debug(2, "%s", __FUNCTION__);
len = sizeof(from);
if ((s = accept(fd, (struct sockaddr*)&from, &len)) < 0){
if ((s = accept(fd, &from, &len)) < 0){
clicon_err(OE_UNIX, errno, "accept");
goto done;
}
#if defined(SO_PEERCRED)
/* fill in the user data structure */
clen = sizeof(credentials);
if(getsockopt(s, SOL_SOCKET, SO_PEERCRED/* XXX finns ej i freebsd*/, &credentials, &clen)){
clicon_err(OE_UNIX, errno, "getsockopt");
if ((ce = backend_client_add(h, &from)) == NULL)
goto done;
}
#endif
if ((ce = backend_client_add(h, (struct sockaddr*)&from)) == NULL)
goto done;
#if defined(SO_PEERCRED)
ce->ce_pid = credentials.pid;
ce->ce_uid = credentials.uid;
#endif
ce->ce_handle = h;
/* check credentials of caller (not properly implemented yet) */
if ((config_group = clicon_sock_group(h)) == NULL){
clicon_err(OE_FATAL, 0, "clicon_sock_group option not set");
goto done;
}
if ((gr = getgrnam(config_group)) != NULL){
i = 0; /* one of mem should correspond to ce->ce_uid */
while ((mem = gr->gr_mem[i++]) != NULL)
;
}
#if 0
{ /* XXX */
int ii;
struct client_entry *c;
for (c = ce_list, ii=0; c; c = c->ce_next, ii++);
clicon_debug(1, "Open client socket (nr:%d pid:%d [Total: %d])",
ce->ce_nr, ce->ce_pid, ii);
}
/*
* Get credentials of connected peer - only for unix socket
*/
switch (from.sa_family){
case AF_UNIX:
#if defined(HAVE_SO_PEERCRED)
clen = sizeof(cr);
if(getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &clen) < 0){
clicon_err(OE_UNIX, errno, "getsockopt");
goto done;
}
ce->ce_pid = cr.pid; /* XXX no use session-id */
if (uid2name(cr.uid, &name) < 0)
goto done;
#elif defined(HAVE_GETPEEREID)
if (getpeereid(s, &euid, &guid) < 0)
goto done;
if (uid2name(euid, &name) < 0)
goto done;
#else
#error "Need getsockopt O_PEERCRED or getpeereid for unix socket peer cred"
#endif
if (name && (ce->ce_username = strdup(name)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
break;
case AF_INET: /* XXX: HACK ce->ce_pid */
ce->ce_pid = 0;
for (i=0; i<len; i++)
ce->ce_pid ^= from.sa_data[i];
break;
case AF_INET6:
default:
break;
}
ce->ce_s = s;
/*

View file

@ -141,7 +141,7 @@ backend_client_add(clicon_handle h,
return NULL;
}
memset(ce, 0, sizeof(*ce));
ce->ce_nr = bh->bh_ce_nr++;
ce->ce_nr = bh->bh_ce_nr++; /* Session-id ? */
memcpy(&ce->ce_addr, addr, sizeof(*addr));
ce->ce_next = bh->bh_ce_list;
bh->bh_ce_list = ce;
@ -177,6 +177,8 @@ backend_client_delete(clicon_handle h,
for (c = *ce_prev; c; c = c->ce_next){
if (c == ce){
*ce_prev = c->ce_next;
if (ce->ce_username)
free(ce->ce_username);
free(ce);
break;
}