authd: initial authentication framework implementation

Not plugged into anything yet, but compile-tested.
This commit is contained in:
Elizabeth Myers 2016-03-10 01:50:36 -06:00
parent 3ec1db3785
commit 05e17ac208
3 changed files with 358 additions and 1 deletions

View file

@ -3,5 +3,5 @@ AM_CFLAGS=$(WARNFLAGS)
AM_CPPFLAGS = -I../include -I../librb/include AM_CPPFLAGS = -I../include -I../librb/include
authd_SOURCES = authd.c res.c reslib.c dns.c authd_SOURCES = auth.c authd.c res.c reslib.c dns.c
authd_LDADD = ../librb/src/librb.la authd_LDADD = ../librb/src/librb.la

250
authd/auth.c Normal file
View file

@ -0,0 +1,250 @@
/* authd/auth.c - authentication provider framework
* Copyright (c) 2016 Elizabeth Myers <elizabeth@interlinked.me>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* So the basic design here is to have "authentication providers" that do
* things like query ident and blacklists and even open proxies.
*
* Providers are registered statically in the struct auth_providers array. You will
* probably want to add an item to the provider_t enum also.
*
* Providers can either return failure immediately, immediate acceptance, or
* do work in the background (calling set_provider to signal this).
*
* It is up to providers to keep their own state on clients if they need to.
*
* All providers must implement at a minimum a perform_provider function. You
* don't have to implement the others if you don't need them.
*
* Providers may kick clients off by rejecting them. Upon rejection, all
* providers are cancelled. They can also unconditionally accept them.
*
* When a provider has done its work, it should call provider_done.
*
* --Elizafox, 9 March 2016
*/
#include "authd.h"
#include "auth.h"
/*****************************************************************************/
/* This is just some test code for the system */
bool dummy_init(void)
{
/* Do a dummy init */
return true;
}
void dummy_destroy(void)
{
/* Do a dummy destroy */
}
bool dummy_start(struct auth_client *auth)
{
/* Set the client's username to testhost as a test */
strcpy(auth->username, "testhost");
return true;
}
void dummy_cancel(struct auth_client *auth)
{
/* Does nothing */
}
/*****************************************************************************/
#define NULL_PROVIDER { \
.provider = PROVIDER_NULL, \
.init = NULL, \
.destroy = NULL, \
.start = NULL, \
.cancel = NULL, \
.completed = NULL, \
}
#define DUMMY_PROVIDER { \
.provider = PROVIDER_DUMMY, \
.init = dummy_init, \
.destroy = dummy_destroy, \
.start = dummy_start, \
.cancel = dummy_cancel, \
.completed = NULL, \
}
/* Providers */
static struct auth_provider auth_providers[] =
{
NULL_PROVIDER,
DUMMY_PROVIDER
};
/* Clients waiting */
struct auth_client auth_clients[MAX_CLIENTS];
/* Initalise all providers */
void init_providers(void)
{
struct auth_provider *pptr;
AUTH_PROVIDER_FOREACH(pptr)
{
if(pptr->init && !pptr->init())
/* Provider failed to init, time to go */
exit(1);
}
}
/* Terminate all providers */
void destroy_providerss(void)
{
struct auth_provider *pptr;
/* Cancel outstanding connections */
for (size_t i = 0; i < MAX_CLIENTS; i++)
{
if(auth_clients[i].cid)
{
/* TBD - is this the right thing?
* (NOTE - this error message is designed for morons) */
reject_client(&auth_clients[i],
"IRC server reloading... try reconnecting in a few seconds");
}
}
AUTH_PROVIDER_FOREACH(pptr)
{
if(pptr->destroy)
pptr->destroy();
}
}
/* Cancel outstanding providers for a client */
void cancel_providers(struct auth_client *auth)
{
struct auth_provider *pptr;
AUTH_PROVIDER_FOREACH(pptr)
{
if(pptr->cancel && is_provider(auth, pptr->provider))
/* Cancel if required */
pptr->cancel(auth);
}
}
/* Provider is done */
void provider_done(struct auth_client *auth, provider_t provider)
{
struct auth_provider *pptr;
unset_provider(auth, provider);
if(!auth->providers)
{
/* No more providers, done */
accept_client(auth);
return;
}
AUTH_PROVIDER_FOREACH(pptr)
{
if(pptr->completed && is_provider(auth, pptr->provider))
/* Notify pending clients who asked for it */
pptr->completed(auth, provider);
}
}
/* Reject a client, cancel outstanding providers if any */
void reject_client(struct auth_client *auth, const char *reason)
{
uint16_t cid = auth->cid;
rb_helper_write(authd_helper, "R %x :%s", auth->cid, reason);
if(auth->providers)
cancel_providers(auth);
memset(&auth_clients[cid], 0, sizeof(struct auth_client));
}
/* Accept a client, cancel outstanding providers if any */
void accept_client(struct auth_client *auth)
{
uint16_t cid = auth->cid;
rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname);
if(auth->providers)
cancel_providers(auth);
memset(&auth_clients[cid], 0, sizeof(struct auth_client));
}
/* Send a notice to a client */
void notice_client(struct auth_client *auth, const char *notice)
{
rb_helper_write(authd_helper, "N %x :%s", auth->cid, notice);
}
/* Begin authenticating user */
void start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_ip, const char *c_port)
{
struct auth_provider *pptr;
struct auth_client *auth;
long lcid = strtol(cid, NULL, 16);
if(lcid >= MAX_CLIENTS)
return;
auth = &auth_clients[lcid];
if(auth->cid != 0)
/* Shouldn't get here */
return;
auth->cid = (uint16_t)lcid;
rb_strlcpy(auth->l_ip, l_ip, sizeof(auth->l_ip));
auth->l_port = (uint16_t)atoi(l_port); /* Safe cast, port shouldn't exceed 16 bits */
rb_strlcpy(auth->c_ip, c_ip, sizeof(auth->c_ip));
auth->c_port = (uint16_t)atoi(c_port);
AUTH_PROVIDER_FOREACH(pptr)
{
/* Execute providers */
if(!pptr->start(auth))
{
/* Rejected immediately */
cancel_providers(auth);
return;
}
}
/* If no providers are running, accept the client */
if(!auth->providers)
accept_client(auth);
}
/* Callback for the initiation */
void handle_new_connection(int parc, char *parv[])
{
if(parc < 5)
return;
start_auth(parv[1], parv[2], parv[3], parv[4], parv[5]);
}

107
authd/auth.h Normal file
View file

@ -0,0 +1,107 @@
/* authd/auth.h - authentication provider framework
* Copyright (c) 2016 Elizabeth Myers <elizabeth@interlinked.me>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __CHARYBDIS_AUTHD_AUTH_H__
#define __CHARYBDIS_AUTHD_AUTH_H__
#include "stdinc.h"
/* Arbitrary limit */
#define MAX_CLIENTS 1024
/* Registered providers */
typedef enum
{
PROVIDER_NULL = 0x0, /* Dummy value */
PROVIDER_RDNS = 0x1,
PROVIDER_IDENT = 0x2,
PROVIDER_BLACKLIST = 0x4,
PROVIDER_DUMMY = 0x8,
} provider_t;
struct auth_client
{
uint16_t cid; /* Client ID */
char l_ip[HOSTIPLEN + 1]; /* Listener IP address */
uint16_t l_port; /* Listener port */
char c_ip[HOSTIPLEN + 1]; /* Client IP address */
uint16_t c_port; /* Client port */
char hostname[IRCD_RES_HOSTLEN + 1]; /* Used for DNS lookup */
char username[USERLEN + 1]; /* Used for ident lookup */
unsigned int providers; /* Providers at work,
* none left when set to 0 */
};
typedef bool (*provider_init_t)(void);
typedef bool (*provider_perform_t)(struct auth_client *);
typedef void (*provider_complete_t)(struct auth_client *, provider_t provider);
typedef void (*provider_cancel_t)(struct auth_client *);
typedef void (*provider_destroy_t)(void);
struct auth_provider
{
provider_t provider;
provider_init_t init; /* Initalise the provider */
provider_destroy_t destroy; /* Terminate the provider */
provider_perform_t start; /* Perform authentication */
provider_cancel_t cancel; /* Authentication cancelled */
provider_complete_t completed; /* Callback for when other performers complete (think dependency chains) */
};
#define AUTH_PROVIDER_FOREACH(a) for((a) = auth_providers; (a)->provider; (a)++)
void init_providers(void);
void destroy_providers(void);
void cancel_providers(struct auth_client *auth);
void provider_done(struct auth_client *auth, provider_t provider);
void reject_client(struct auth_client *auth, const char *reason);
void accept_client(struct auth_client *auth);
void notice_client(struct auth_client *auth, const char *notice);
void start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_ip, const char *c_port);
void handle_new_connection(int parc, char *parv[]);
/* Provider is operating on this auth_client (set this if you have async work to do) */
static inline void set_provider(struct auth_client *auth, provider_t provider)
{
auth->providers |= provider;
}
/* Provider is no longer operating on this auth client (you should use provider_done) */
static inline void unset_provider(struct auth_client *auth, provider_t provider)
{
auth->providers &= ~provider;
}
/* Check if provider is operating on this auth client */
static inline bool is_provider(struct auth_client *auth, provider_t provider)
{
return auth->providers & provider;
}
#endif /* __CHARYBDIS_AUTHD_AUTH_H__ */