Tip of the day: The Security article gives hands-on tips on how to deal with drone attacks, flooding, spammers, (D)DoS and more.

Dev:Authentication module

From UnrealIRCd documentation wiki
Jump to navigation Jump to search

Usually, you use Services which provide SASL. Sometimes, a (third party) module may want to provide SASL locally instead.

Note that at the time of writing this is still a dumb interface and not a real proper authentication framework.

To do so, in your module:

  • First, point set::sasl-server to your own servername (as shown in the module below in MOD_LOAD).
  • Hook into HOOKTYPE_SASL_MECHS and tell what SASL mechanisms you support
  • Hook into HOOKTYPE_SASL_AUTHENTICATE:
    • Check the authentication mechanism if first==1 and set client->local->sasl_agent if you are OK with it.
    • Do the authentication when first==0, you can use the decode_authenticate_plain() helper function
      • If succeeded: set client->user->account, call user_account_login() and sasl_succeeded()
      • If rejected: call sasl_failed()
/* SASL Authentication Example Backend Module
 * (C) Copyright 2024 Bram Matthys ("Syzop")
 * License: GPLv2 or later
 *
 * This is a simple local SASL backend example module for
 * illustrative purposes only.
 * If you log in with SASL, method AUTHENTICATE PLAIN,
 * with the account "Example" and password "test"
 * (both are case sensitive) then SASL authentication will
 * succeed and you will be logged into the account "Example".
 *
 * This module does not do anything else. In particular
 * it does not provide any nick-related functions such as
 * nick ownership nor does it set user mode +r.
 */

#include "unrealircd.h"

ModuleHeader MOD_HEADER
  = {
	"third/auth_example",
	"1.0.0",
	"SASL authentication example backend module",
	"UnrealIRCd Team",
	"unrealircd-6",
    };

/* Forward declarations */
int auth_example_sasl_authenticate(Client *client, int first, const char *param);
const char *auth_example_sasl_mechs(Client *client);

MOD_INIT()
{
	ModDataInfo mreq;

	HookAdd(modinfo->handle, HOOKTYPE_SASL_AUTHENTICATE, 0, auth_example_sasl_authenticate);
	HookAddConstString(modinfo->handle, HOOKTYPE_SASL_MECHS, 0, auth_example_sasl_mechs);
	return MOD_SUCCESS;
}

MOD_LOAD()
{
	/* Set the set::sasl-server to our server name */
	safe_strdup(iConf.sasl_server, me.name);
	return MOD_SUCCESS;
}


MOD_UNLOAD()
{
	return MOD_SUCCESS;
}

int auth_example_check_auth(const char *username, const char *passwd)
{
	if (!strcmp(username, "Example") && !strcmp(passwd, "test"))
		return 1;
	return 0;
}

const char *auth_example_sasl_mechs(Client *client)
{
	return "PLAIN";
}

int auth_example_sasl_authenticate(Client *client, int first, const char *param)
{
	char *authorization_id = NULL;
	char *authentication_id = NULL;
	char *passwd = NULL;

	if (first)
	{
		if (!strcmp(param, "PLAIN"))
		{
			/* Yup, we'll take that request */
			strlcpy(client->local->sasl_agent, me.name, sizeof(client->local->sasl_agent));
			sendto_one(client, NULL, "AUTHENTICATE +");
		} else {
			sasl_failed(client);
		}
		return 0;
	}

	/* Else it is a continuation and we can do the authentication now */
	if (!decode_authenticate_plain(param, &authorization_id, &authentication_id, &passwd))
	{
		sasl_failed(client);
		return 0;
	}

	if (auth_example_check_auth(authentication_id, passwd))
	{
		strlcpy(client->user->account, authentication_id, sizeof(client->user->account));
		user_account_login(NULL, client); // mtags is NULL here because we don't have it
		if (IsDead(client))
			return 0; /* was killed due to *LINE on ~account probably */
		sasl_succeeded(client);
	} else {
		sasl_failed(client);
	}

	return 0;
}