mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-19 02:10:42 +01:00
Remove the use sizeof(char) or sizeof(uchar) in calculations for memory allocation or string length. There are no known platforms for which sizeof(char) or sizeof(uchar) is not 1, and c99 has defined sizeof(char) to be 1 (section 6.5.3.4 of c99).
809 lines
22 KiB
C
809 lines
22 KiB
C
/* imgssapi.c
|
|
* This is the implementation of the GSSAPI input module.
|
|
*
|
|
* Note: the root gssapi code was contributed by varmojfekoj and is most often
|
|
* maintened by him. I am just doing the plumbing around it (I event don't have a
|
|
* test lab for gssapi yet... ). I am very grateful for this useful code
|
|
* contribution -- rgerhards, 2008-03-05
|
|
*
|
|
* NOTE: read comments in module-template.h to understand how this file
|
|
* works!
|
|
*
|
|
* Copyright 2007, 2014 Rainer Gerhards and Adiscon GmbH.
|
|
*
|
|
* This file is part of rsyslog.
|
|
*
|
|
* Rsyslog 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.
|
|
*
|
|
* Rsyslog 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 Rsyslog. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* A copy of the GPL can be found in the file "COPYING" in this distribution.
|
|
*/
|
|
#include "config.h"
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#if HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#include <gssapi/gssapi.h>
|
|
#include "rsyslog.h"
|
|
#include "dirty.h"
|
|
#include "cfsysline.h"
|
|
#include "module-template.h"
|
|
#include "unicode-helper.h"
|
|
#include "net.h"
|
|
#include "srUtils.h"
|
|
#include "gss-misc.h"
|
|
#include "tcpsrv.h"
|
|
#include "tcps_sess.h"
|
|
#include "errmsg.h"
|
|
#include "netstrm.h"
|
|
#include "glbl.h"
|
|
#include "debug.h"
|
|
#include "unlimited_select.h"
|
|
|
|
|
|
MODULE_TYPE_INPUT
|
|
MODULE_TYPE_NOKEEP
|
|
MODULE_CNFNAME("imgssapi")
|
|
|
|
/* defines */
|
|
#define ALLOWEDMETHOD_GSS 2
|
|
#define ALLOWEDMETHOD_TCP 1
|
|
|
|
|
|
/* some forward definitions - they may go away when we no longer include imtcp.c */
|
|
static rsRetVal addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal);
|
|
static rsRetVal actGSSListener(uchar *port);
|
|
static int TCPSessGSSInit(void);
|
|
static void TCPSessGSSClose(tcps_sess_t* pSess);
|
|
static rsRetVal TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len, ssize_t *);
|
|
static rsRetVal onSessAccept(tcpsrv_t *pThis, tcps_sess_t *ppSess);
|
|
static rsRetVal OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *ppSess);
|
|
|
|
/* static data */
|
|
DEF_IMOD_STATIC_DATA
|
|
DEFobjCurrIf(tcpsrv)
|
|
DEFobjCurrIf(tcps_sess)
|
|
DEFobjCurrIf(gssutil)
|
|
DEFobjCurrIf(errmsg)
|
|
DEFobjCurrIf(netstrm)
|
|
DEFobjCurrIf(net)
|
|
DEFobjCurrIf(glbl)
|
|
DEFobjCurrIf(prop)
|
|
|
|
static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
|
|
static gss_cred_id_t gss_server_creds = GSS_C_NO_CREDENTIAL;
|
|
static uchar *srvPort;
|
|
|
|
/* our usr structure for the tcpsrv object */
|
|
typedef struct gsssrv_s {
|
|
char allowedMethods;
|
|
} gsssrv_t;
|
|
|
|
/* our usr structure for the session object */
|
|
typedef struct gss_sess_s {
|
|
OM_uint32 gss_flags;
|
|
gss_ctx_id_t gss_context;
|
|
char allowedMethods;
|
|
} gss_sess_t;
|
|
|
|
|
|
/* config variables */
|
|
struct modConfData_s {
|
|
EMPTY_STRUCT;
|
|
};
|
|
|
|
static int iTCPSessMax = 200; /* max number of sessions */
|
|
static char *gss_listen_service_name = NULL;
|
|
static int bPermitPlainTcp = 0; /* plain tcp syslog allowed on GSSAPI port? */
|
|
static int bKeepAlive = 0; /* use SO_KEEPALIVE? */
|
|
|
|
|
|
/* methods */
|
|
/* callbacks */
|
|
static rsRetVal OnSessConstructFinalize(void *ppUsr)
|
|
{
|
|
DEFiRet;
|
|
gss_sess_t **ppGSess = (gss_sess_t**) ppUsr;
|
|
gss_sess_t *pGSess;
|
|
|
|
assert(ppGSess != NULL);
|
|
|
|
if((pGSess = calloc(1, sizeof(gss_sess_t))) == NULL)
|
|
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
|
|
|
|
pGSess->gss_flags = 0;
|
|
pGSess->gss_context = GSS_C_NO_CONTEXT;
|
|
pGSess->allowedMethods = 0;
|
|
|
|
*ppGSess = pGSess;
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Destruct the user session pointer for a GSSAPI session. Please note
|
|
* that it *is* valid to receive a NULL user pointer. In this case, the
|
|
* sessions is to be torn down before it was fully initialized. This
|
|
* happens in error cases, e.g. when the host ACL did not match.
|
|
* rgerhards, 2008-03-03
|
|
*/
|
|
static rsRetVal
|
|
OnSessDestruct(void *ppUsr)
|
|
{
|
|
DEFiRet;
|
|
gss_sess_t **ppGSess = (gss_sess_t**) ppUsr;
|
|
|
|
assert(ppGSess != NULL);
|
|
if(*ppGSess == NULL)
|
|
FINALIZE;
|
|
|
|
if((*ppGSess)->allowedMethods & ALLOWEDMETHOD_GSS) {
|
|
OM_uint32 maj_stat, min_stat;
|
|
maj_stat = gss_delete_sec_context(&min_stat, &(*ppGSess)->gss_context, GSS_C_NO_BUFFER);
|
|
if (maj_stat != GSS_S_COMPLETE)
|
|
gssutil.display_status("deleting context", maj_stat, min_stat);
|
|
}
|
|
|
|
free(*ppGSess);
|
|
*ppGSess = NULL;
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Check if the host is permitted to send us messages.
|
|
* Note: the pUsrSess may be zero if the server is running in tcp-only mode!
|
|
*/
|
|
static int
|
|
isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void *pUsrSrv, void*pUsrSess)
|
|
{
|
|
gsssrv_t *pGSrv;
|
|
gss_sess_t *pGSess;
|
|
char allowedMethods = 0;
|
|
|
|
BEGINfunc
|
|
assert(pUsrSrv != NULL);
|
|
pGSrv = (gsssrv_t*) pUsrSrv;
|
|
pGSess = (gss_sess_t*) pUsrSess;
|
|
|
|
if((pGSrv->allowedMethods & ALLOWEDMETHOD_TCP) &&
|
|
net.isAllowedSender2((uchar*)"TCP", addr, (char*)fromHostFQDN, 1))
|
|
allowedMethods |= ALLOWEDMETHOD_TCP;
|
|
if((pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) &&
|
|
net.isAllowedSender2((uchar*)"GSS", addr, (char*)fromHostFQDN, 1))
|
|
allowedMethods |= ALLOWEDMETHOD_GSS;
|
|
if(allowedMethods && pGSess != NULL)
|
|
pGSess->allowedMethods = allowedMethods;
|
|
ENDfunc
|
|
return allowedMethods;
|
|
}
|
|
|
|
|
|
static rsRetVal
|
|
onSessAccept(tcpsrv_t *pThis, tcps_sess_t *pSess)
|
|
{
|
|
DEFiRet;
|
|
gsssrv_t *pGSrv;
|
|
|
|
pGSrv = (gsssrv_t*) pThis->pUsr;
|
|
|
|
if(pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) {
|
|
iRet = OnSessAcceptGSS(pThis, pSess);
|
|
}
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
static rsRetVal
|
|
onRegularClose(tcps_sess_t *pSess)
|
|
{
|
|
DEFiRet;
|
|
gss_sess_t *pGSess;
|
|
|
|
assert(pSess != NULL);
|
|
assert(pSess->pUsr != NULL);
|
|
pGSess = (gss_sess_t*) pSess->pUsr;
|
|
|
|
if(pGSess->allowedMethods & ALLOWEDMETHOD_GSS)
|
|
TCPSessGSSClose(pSess);
|
|
else {
|
|
/* process any incomplete frames left over */
|
|
tcps_sess.PrepareClose(pSess);
|
|
/* Session closed */
|
|
tcps_sess.Close(pSess);
|
|
}
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
static rsRetVal
|
|
onErrClose(tcps_sess_t *pSess)
|
|
{
|
|
DEFiRet;
|
|
gss_sess_t *pGSess;
|
|
|
|
assert(pSess != NULL);
|
|
assert(pSess->pUsr != NULL);
|
|
pGSess = (gss_sess_t*) pSess->pUsr;
|
|
|
|
if(pGSess->allowedMethods & ALLOWEDMETHOD_GSS)
|
|
TCPSessGSSClose(pSess);
|
|
else
|
|
tcps_sess.Close(pSess);
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* open the listen sockets */
|
|
static rsRetVal
|
|
doOpenLstnSocks(tcpsrv_t *pSrv)
|
|
{
|
|
gsssrv_t *pGSrv;
|
|
DEFiRet;
|
|
|
|
ISOBJ_TYPE_assert(pSrv, tcpsrv);
|
|
pGSrv = pSrv->pUsr;
|
|
assert(pGSrv != NULL);
|
|
|
|
/* first apply some config settings */
|
|
if(pGSrv->allowedMethods) {
|
|
if(pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) {
|
|
if(TCPSessGSSInit()) {
|
|
errmsg.LogError(0, NO_ERRCODE, "GSS-API initialization failed\n");
|
|
pGSrv->allowedMethods &= ~(ALLOWEDMETHOD_GSS);
|
|
}
|
|
}
|
|
if(pGSrv->allowedMethods) {
|
|
/* fallback to plain TCP */
|
|
CHKiRet(tcpsrv.create_tcp_socket(pSrv));
|
|
} else {
|
|
ABORT_FINALIZE(RS_RET_GSS_ERR);
|
|
}
|
|
}
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
static rsRetVal
|
|
doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf, ssize_t *piLenRcvd)
|
|
{
|
|
DEFiRet;
|
|
int allowedMethods;
|
|
gss_sess_t *pGSess;
|
|
|
|
assert(pSess != NULL);
|
|
assert(pSess->pUsr != NULL);
|
|
pGSess = (gss_sess_t*) pSess->pUsr;
|
|
assert(piLenRcvd != NULL);
|
|
|
|
allowedMethods = pGSess->allowedMethods;
|
|
if(allowedMethods & ALLOWEDMETHOD_GSS) {
|
|
CHKiRet(TCPSessGSSRecv(pSess, buf, lenBuf, piLenRcvd));
|
|
} else {
|
|
*piLenRcvd = lenBuf;
|
|
CHKiRet(netstrm.Rcv(pSess->pStrm, (uchar*) buf, piLenRcvd) != RS_RET_OK);
|
|
}
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* end callbacks */
|
|
|
|
static rsRetVal
|
|
addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
|
|
{
|
|
DEFiRet;
|
|
|
|
srvPort = pNewVal;
|
|
|
|
RETiRet;
|
|
}
|
|
|
|
static rsRetVal
|
|
actGSSListener(uchar *port)
|
|
{
|
|
DEFiRet;
|
|
gsssrv_t *pGSrv;
|
|
|
|
if(pOurTcpsrv == NULL) {
|
|
/* first create/init the gsssrv "object" */
|
|
if((pGSrv = calloc(1, sizeof(gsssrv_t))) == NULL)
|
|
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
|
|
|
|
pGSrv->allowedMethods = ALLOWEDMETHOD_GSS;
|
|
if(bPermitPlainTcp)
|
|
pGSrv->allowedMethods |= ALLOWEDMETHOD_TCP;
|
|
/* gsssrv initialized */
|
|
|
|
CHKiRet(tcpsrv.Construct(&pOurTcpsrv));
|
|
CHKiRet(tcpsrv.SetUsrP(pOurTcpsrv, pGSrv));
|
|
CHKiRet(tcpsrv.SetCBOnSessConstructFinalize(pOurTcpsrv, OnSessConstructFinalize));
|
|
CHKiRet(tcpsrv.SetCBOnSessDestruct(pOurTcpsrv, OnSessDestruct));
|
|
CHKiRet(tcpsrv.SetCBIsPermittedHost(pOurTcpsrv, isPermittedHost));
|
|
CHKiRet(tcpsrv.SetCBRcvData(pOurTcpsrv, doRcvData));
|
|
CHKiRet(tcpsrv.SetCBOpenLstnSocks(pOurTcpsrv, doOpenLstnSocks));
|
|
CHKiRet(tcpsrv.SetCBOnSessAccept(pOurTcpsrv, onSessAccept));
|
|
CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
|
|
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
|
|
CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, UCHAR_CONSTANT("imgssapi")));
|
|
CHKiRet(tcpsrv.SetKeepAlive(pOurTcpsrv, bKeepAlive));
|
|
CHKiRet(tcpsrv.SetOrigin(pOurTcpsrv, UCHAR_CONSTANT("imgssapi")));
|
|
tcpsrv.configureTCPListen(pOurTcpsrv, port, 1, NULL);
|
|
CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
|
|
}
|
|
|
|
finalize_it:
|
|
if(iRet != RS_RET_OK) {
|
|
errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
|
|
if(pOurTcpsrv != NULL)
|
|
tcpsrv.Destruct(&pOurTcpsrv);
|
|
}
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* returns 0 if all went OK, -1 if it failed */
|
|
static int TCPSessGSSInit(void)
|
|
{
|
|
gss_buffer_desc name_buf;
|
|
gss_name_t server_name;
|
|
OM_uint32 maj_stat, min_stat;
|
|
|
|
if (gss_server_creds != GSS_C_NO_CREDENTIAL)
|
|
return 0;
|
|
|
|
name_buf.value = (gss_listen_service_name == NULL) ? "host" : gss_listen_service_name;
|
|
name_buf.length = strlen(name_buf.value) + 1;
|
|
maj_stat = gss_import_name(&min_stat, &name_buf, GSS_C_NT_HOSTBASED_SERVICE, &server_name);
|
|
if (maj_stat != GSS_S_COMPLETE) {
|
|
gssutil.display_status("importing name", maj_stat, min_stat);
|
|
return -1;
|
|
}
|
|
|
|
maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
|
|
GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
|
|
&gss_server_creds, NULL, NULL);
|
|
if (maj_stat != GSS_S_COMPLETE) {
|
|
gssutil.display_status("acquiring credentials", maj_stat, min_stat);
|
|
return -1;
|
|
}
|
|
|
|
gss_release_name(&min_stat, &server_name);
|
|
dbgprintf("GSS-API initialized\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* returns 0 if all went OK, -1 if it failed
|
|
* tries to guess if the connection uses gssapi.
|
|
*/
|
|
static rsRetVal
|
|
OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess)
|
|
{
|
|
DEFiRet;
|
|
gss_buffer_desc send_tok, recv_tok;
|
|
gss_name_t client;
|
|
OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
|
|
gss_ctx_id_t *context;
|
|
OM_uint32 *sess_flags;
|
|
int fdSess;
|
|
char allowedMethods;
|
|
gsssrv_t *pGSrv;
|
|
gss_sess_t *pGSess;
|
|
uchar *pszPeer = NULL;
|
|
int lenPeer = 0;
|
|
|
|
assert(pSess != NULL);
|
|
|
|
pGSrv = (gsssrv_t*) pThis->pUsr;
|
|
pGSess = (gss_sess_t*) pSess->pUsr;
|
|
allowedMethods = pGSrv->allowedMethods;
|
|
if(allowedMethods & ALLOWEDMETHOD_GSS) {
|
|
/* Buffer to store raw message in case that
|
|
* gss authentication fails halfway through. This buffer
|
|
* is currently dynamically allocated, for performance
|
|
* reasons we should look for a better way to do it.
|
|
* rgerhars, 2008-09-02
|
|
*/
|
|
char *buf;
|
|
int ret = 0;
|
|
CHKmalloc(buf = (char*) MALLOC(glbl.GetMaxLine() + 1));
|
|
|
|
prop.GetString(pSess->fromHostIP, &pszPeer, &lenPeer);
|
|
|
|
dbgprintf("GSS-API Trying to accept TCP session %p from %s\n", pSess, (char *)pszPeer);
|
|
|
|
CHKiRet(netstrm.GetSock(pSess->pStrm, &fdSess)); // TODO: method access!
|
|
if (allowedMethods & ALLOWEDMETHOD_TCP) {
|
|
int len;
|
|
struct timeval tv;
|
|
#ifdef USE_UNLIMITED_SELECT
|
|
fd_set *pFds = malloc(glbl.GetFdSetSize());
|
|
#else
|
|
fd_set fds;
|
|
fd_set *pFds = &fds;
|
|
#endif
|
|
|
|
do {
|
|
FD_ZERO(pFds);
|
|
FD_SET(fdSess, pFds);
|
|
tv.tv_sec = 1;
|
|
tv.tv_usec = 0;
|
|
ret = select(fdSess + 1, pFds, NULL, NULL, &tv);
|
|
} while (ret < 0 && errno == EINTR);
|
|
if (ret < 0) {
|
|
errmsg.LogError(0, RS_RET_ERR, "TCP session %p from %s will be closed, error ignored\n", pSess, (char *)pszPeer);
|
|
ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
|
|
} else if (ret == 0) {
|
|
dbgprintf("GSS-API Reverting to plain TCP\n");
|
|
pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
|
|
ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
|
|
}
|
|
|
|
do {
|
|
ret = recv(fdSess, buf, sizeof (buf), MSG_PEEK);
|
|
} while (ret < 0 && errno == EINTR);
|
|
if (ret <= 0) {
|
|
if (ret == 0) {
|
|
dbgprintf("GSS-API Connection closed by peer %s\n", (char *)pszPeer);
|
|
} else {
|
|
errmsg.LogError(0, RS_RET_ERR, "TCP(GSS) session %p from %s will be closed, error ignored\n", pSess, (char *)pszPeer);
|
|
}
|
|
ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
|
|
}
|
|
|
|
if (ret < 4) {
|
|
dbgprintf("GSS-API Reverting to plain TCP from %s\n", (char *)pszPeer);
|
|
pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
|
|
ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
|
|
} else if (ret == 4) {
|
|
/* The client might has been interupted after sending
|
|
* the data length (4B), give him another chance.
|
|
*/
|
|
srSleep(1, 0);
|
|
do {
|
|
ret = recv(fdSess, buf, sizeof (buf), MSG_PEEK);
|
|
} while (ret < 0 && errno == EINTR);
|
|
if (ret <= 0) {
|
|
if (ret == 0) {
|
|
dbgprintf("GSS-API Connection closed by peer %s\n", (char *)pszPeer);
|
|
} else {
|
|
errmsg.LogError(0, NO_ERRCODE, "TCP session %p from %s will be closed, error ignored\n", pSess, (char *)pszPeer);
|
|
}
|
|
ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
|
|
}
|
|
}
|
|
|
|
/* TODO: how does this work together with IPv6? Does it? */
|
|
len = ntohl((buf[0] << 24)
|
|
| (buf[1] << 16)
|
|
| (buf[2] << 8)
|
|
| buf[3]);
|
|
if ((ret - 4) < len || len == 0) {
|
|
dbgprintf("GSS-API Reverting to plain TCP from %s\n", (char *)pszPeer);
|
|
pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
|
|
ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
|
|
}
|
|
|
|
freeFdSet(pFds);
|
|
}
|
|
|
|
context = &pGSess->gss_context;
|
|
*context = GSS_C_NO_CONTEXT;
|
|
sess_flags = &pGSess->gss_flags;
|
|
do {
|
|
if (gssutil.recv_token(fdSess, &recv_tok) <= 0) {
|
|
errmsg.LogError(0, NO_ERRCODE, "TCP session %p from %s will be closed, error ignored\n", pSess, (char *)pszPeer);
|
|
ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
|
|
}
|
|
maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context, gss_server_creds,
|
|
&recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &client,
|
|
NULL, &send_tok, sess_flags, NULL, NULL);
|
|
if (recv_tok.value) {
|
|
free(recv_tok.value);
|
|
recv_tok.value = NULL;
|
|
}
|
|
if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
|
|
gss_release_buffer(&min_stat, &send_tok);
|
|
if (*context != GSS_C_NO_CONTEXT)
|
|
gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
|
|
if ((allowedMethods & ALLOWEDMETHOD_TCP) &&
|
|
(GSS_ROUTINE_ERROR(maj_stat) == GSS_S_DEFECTIVE_TOKEN)) {
|
|
dbgprintf("GSS-API Reverting to plain TCP from %s\n", (char *)pszPeer);
|
|
dbgprintf("tcp session socket with new data: #%d\n", fdSess);
|
|
if(tcps_sess.DataRcvd(pSess, buf, ret) != RS_RET_OK) {
|
|
errmsg.LogError(0, NO_ERRCODE, "Tearing down TCP Session %p from %s - see "
|
|
"previous messages for reason(s)\n", pSess, (char *)pszPeer);
|
|
ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
|
|
}
|
|
pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
|
|
ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
|
|
}
|
|
gssutil.display_status("accepting context", maj_stat, acc_sec_min_stat);
|
|
ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
|
|
}
|
|
if (send_tok.length != 0) {
|
|
if(gssutil.send_token(fdSess, &send_tok) < 0) {
|
|
gss_release_buffer(&min_stat, &send_tok);
|
|
errmsg.LogError(0, NO_ERRCODE, "TCP session %p from %s will be closed, error ignored\n", pSess, (char *)pszPeer);
|
|
if (*context != GSS_C_NO_CONTEXT)
|
|
gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
|
|
ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
|
|
}
|
|
gss_release_buffer(&min_stat, &send_tok);
|
|
}
|
|
} while (maj_stat == GSS_S_CONTINUE_NEEDED);
|
|
|
|
maj_stat = gss_display_name(&min_stat, client, &recv_tok, NULL);
|
|
if (maj_stat != GSS_S_COMPLETE) {
|
|
gssutil.display_status("displaying name", maj_stat, min_stat);
|
|
} else {
|
|
dbgprintf("GSS-API Accepted connection from peer %s: %s\n", (char *)pszPeer, (char*) recv_tok.value);
|
|
}
|
|
gss_release_name(&min_stat, &client);
|
|
gss_release_buffer(&min_stat, &recv_tok);
|
|
|
|
dbgprintf("GSS-API Provided context flags:\n");
|
|
gssutil.display_ctx_flags(*sess_flags);
|
|
pGSess->allowedMethods = ALLOWEDMETHOD_GSS;
|
|
}
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Replaces recv() for gssapi connections.
|
|
*/
|
|
int TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len, ssize_t *piLenRcvd)
|
|
{
|
|
DEFiRet;
|
|
gss_buffer_desc xmit_buf, msg_buf;
|
|
gss_ctx_id_t *context;
|
|
OM_uint32 maj_stat, min_stat;
|
|
int fdSess;
|
|
int conf_state;
|
|
int state;
|
|
gss_sess_t *pGSess;
|
|
|
|
assert(pSess->pUsr != NULL);
|
|
assert(piLenRcvd != NULL);
|
|
pGSess = (gss_sess_t*) pSess->pUsr;
|
|
|
|
netstrm.GetSock(pSess->pStrm, &fdSess); // TODO: method access, CHKiRet!
|
|
if ((state = gssutil.recv_token(fdSess, &xmit_buf)) <= 0)
|
|
ABORT_FINALIZE(RS_RET_GSS_ERR);
|
|
|
|
context = &pGSess->gss_context;
|
|
maj_stat = gss_unwrap(&min_stat, *context, &xmit_buf, &msg_buf,
|
|
&conf_state, (gss_qop_t *) NULL);
|
|
if(maj_stat != GSS_S_COMPLETE) {
|
|
gssutil.display_status("unsealing message", maj_stat, min_stat);
|
|
if(xmit_buf.value) {
|
|
free(xmit_buf.value);
|
|
xmit_buf.value = 0;
|
|
}
|
|
ABORT_FINALIZE(RS_RET_GSS_ERR);
|
|
}
|
|
if (xmit_buf.value) {
|
|
free(xmit_buf.value);
|
|
xmit_buf.value = 0;
|
|
}
|
|
|
|
*piLenRcvd = msg_buf.length < buf_len ? msg_buf.length : buf_len;
|
|
memcpy(buf, msg_buf.value, *piLenRcvd);
|
|
gss_release_buffer(&min_stat, &msg_buf);
|
|
|
|
finalize_it:
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
/* Takes care of cleaning up gssapi stuff and then calls
|
|
* TCPSessClose().
|
|
*/
|
|
void TCPSessGSSClose(tcps_sess_t* pSess)
|
|
{
|
|
OM_uint32 maj_stat, min_stat;
|
|
gss_ctx_id_t *context;
|
|
gss_sess_t *pGSess;
|
|
|
|
assert(pSess->pUsr != NULL);
|
|
pGSess = (gss_sess_t*) pSess->pUsr;
|
|
|
|
context = &pGSess->gss_context;
|
|
maj_stat = gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
|
|
if (maj_stat != GSS_S_COMPLETE)
|
|
gssutil.display_status("deleting context", maj_stat, min_stat);
|
|
*context = GSS_C_NO_CONTEXT;
|
|
pGSess->gss_flags = 0;
|
|
pGSess->allowedMethods = 0;
|
|
|
|
tcps_sess.Close(pSess);
|
|
}
|
|
|
|
|
|
/* Counterpart of TCPSessGSSInit(). This is called to exit the GSS system
|
|
* at all. It is a server-based session exit.
|
|
*/
|
|
static rsRetVal
|
|
TCPSessGSSDeinit(void)
|
|
{
|
|
DEFiRet;
|
|
OM_uint32 maj_stat, min_stat;
|
|
|
|
if (gss_server_creds != GSS_C_NO_CREDENTIAL) {
|
|
maj_stat = gss_release_cred(&min_stat, &gss_server_creds);
|
|
if (maj_stat != GSS_S_COMPLETE)
|
|
gssutil.display_status("releasing credentials", maj_stat, min_stat);
|
|
}
|
|
RETiRet;
|
|
}
|
|
|
|
|
|
#if 0 /* can be used to integrate into new config system */
|
|
BEGINbeginCnfLoad
|
|
CODESTARTbeginCnfLoad
|
|
ENDbeginCnfLoad
|
|
|
|
|
|
BEGINendCnfLoad
|
|
CODESTARTendCnfLoad
|
|
ENDendCnfLoad
|
|
|
|
|
|
BEGINcheckCnf
|
|
CODESTARTcheckCnf
|
|
ENDcheckCnf
|
|
|
|
|
|
BEGINactivateCnf
|
|
CODESTARTactivateCnf
|
|
ENDactivateCnf
|
|
|
|
|
|
BEGINfreeCnf
|
|
CODESTARTfreeCnf
|
|
ENDfreeCnf
|
|
#endif
|
|
|
|
/* This function is called to gather input.
|
|
*/
|
|
BEGINrunInput
|
|
CODESTARTrunInput
|
|
/* This will fail if the priviledges are dropped. Should be
|
|
* moved to the '*activateCnfPrePrivDrop' section eventually.
|
|
*/
|
|
actGSSListener(srvPort);
|
|
|
|
iRet = tcpsrv.Run(pOurTcpsrv);
|
|
ENDrunInput
|
|
|
|
|
|
/* initialize and return if will run or not */
|
|
BEGINwillRun
|
|
CODESTARTwillRun
|
|
if(srvPort == NULL)
|
|
ABORT_FINALIZE(RS_RET_NO_RUN);
|
|
|
|
net.PrintAllowedSenders(2); /* TCP */
|
|
net.PrintAllowedSenders(3); /* GSS */
|
|
finalize_it:
|
|
ENDwillRun
|
|
|
|
|
|
|
|
BEGINmodExit
|
|
CODESTARTmodExit
|
|
if(pOurTcpsrv != NULL)
|
|
iRet = tcpsrv.Destruct(&pOurTcpsrv);
|
|
TCPSessGSSDeinit();
|
|
|
|
/* release objects we used */
|
|
objRelease(tcps_sess, LM_TCPSRV_FILENAME);
|
|
objRelease(tcpsrv, LM_TCPSRV_FILENAME);
|
|
objRelease(gssutil, LM_GSSUTIL_FILENAME);
|
|
objRelease(errmsg, CORE_COMPONENT);
|
|
objRelease(glbl, CORE_COMPONENT);
|
|
objRelease(netstrm, LM_NETSTRM_FILENAME);
|
|
objRelease(net, LM_NET_FILENAME);
|
|
objRelease(prop, CORE_COMPONENT);
|
|
ENDmodExit
|
|
|
|
|
|
BEGINafterRun
|
|
CODESTARTafterRun
|
|
/* do cleanup here */
|
|
net.clearAllowedSenders((uchar*)"TCP");
|
|
net.clearAllowedSenders((uchar*)"GSS");
|
|
ENDafterRun
|
|
|
|
|
|
BEGINisCompatibleWithFeature
|
|
CODESTARTisCompatibleWithFeature
|
|
if(eFeat == sFEATURENonCancelInputTermination)
|
|
iRet = RS_RET_OK;
|
|
ENDisCompatibleWithFeature
|
|
|
|
|
|
BEGINqueryEtryPt
|
|
CODESTARTqueryEtryPt
|
|
CODEqueryEtryPt_STD_IMOD_QUERIES
|
|
CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
|
|
ENDqueryEtryPt
|
|
|
|
|
|
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
|
|
{
|
|
if (gss_listen_service_name != NULL) {
|
|
free(gss_listen_service_name);
|
|
gss_listen_service_name = NULL;
|
|
}
|
|
bPermitPlainTcp = 0;
|
|
iTCPSessMax = 200;
|
|
bKeepAlive = 0;
|
|
return RS_RET_OK;
|
|
}
|
|
|
|
|
|
BEGINmodInit()
|
|
CODESTARTmodInit
|
|
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current definition */
|
|
CODEmodInit_QueryRegCFSLineHdlr
|
|
pOurTcpsrv = NULL;
|
|
/* request objects we use */
|
|
CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
|
|
CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
|
|
CHKiRet(objUse(gssutil, LM_GSSUTIL_FILENAME));
|
|
CHKiRet(objUse(errmsg, CORE_COMPONENT));
|
|
CHKiRet(objUse(glbl, CORE_COMPONENT));
|
|
CHKiRet(objUse(netstrm, LM_NETSTRM_FILENAME));
|
|
CHKiRet(objUse(net, LM_NET_FILENAME));
|
|
CHKiRet(objUse(prop, CORE_COMPONENT));
|
|
|
|
/* register config file handlers */
|
|
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssserverpermitplaintcp", 0, eCmdHdlrBinary,
|
|
NULL, &bPermitPlainTcp, STD_LOADABLE_MODULE_ID));
|
|
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssserverrun", 0, eCmdHdlrGetWord,
|
|
addGSSListener, NULL, STD_LOADABLE_MODULE_ID));
|
|
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssserverservicename", 0, eCmdHdlrGetWord,
|
|
NULL, &gss_listen_service_name, STD_LOADABLE_MODULE_ID));
|
|
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssservermaxsessions", 0, eCmdHdlrInt,
|
|
NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
|
|
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssserverkeepalive", 0, eCmdHdlrBinary,
|
|
NULL, &bKeepAlive, STD_LOADABLE_MODULE_ID));
|
|
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
|
|
resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
|
|
ENDmodInit
|
|
|
|
/* vim:set ai:
|
|
*/
|