Changeset 8e6ecfe


Ignore:
Timestamp:
2016-03-25T18:07:53Z (3 years ago)
Author:
Dennis Kaarsemaker <dennis@…>
Branches:
master
Children:
a6005da
Parents:
446a23e
git-author:
Dennis Kaarsemaker <dennis@…> (23-02-16 18:41:34)
git-committer:
Dennis Kaarsemaker <dennis@…> (25-03-16 18:07:53)
Message:

Authentication: scaffolding for multiple authentication backends

Instead of always putting users passwords in XML files, allow site
admins to configure a different authentication method to integrate
authentication with other systems.

This doesn't add any authentication backends yet, merely the
scaffolding. Notably:

  • Password checking and loading/removing from storage has been decoupled. A new auth_check_pass function is used to check passwords. It does check against the configured storage first, but will handle the authentication backends as well. The XML storage merely signals that a user's password should be checked using an authentication backend.
  • If unknown-to-bitlbee users identify using an authentication backend, they are automatically registered.
  • If an authentication backend is used, that fact is stored in the XML file, the password is not. Passwords are also stored unencrypted in this case, as the password used to encrypt them can change underneath us.
  • configure and Makefile changes for the backend objects
Files:
2 added
14 edited

Legend:

Unmodified
Added
Removed
  • Makefile

    r446a23e r8e6ecfe  
    1010
    1111# Program variables
    12 objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o conf.o log.o
     12objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) auth.o $(AUTH_OBJS) unix.o conf.o log.o
    1313headers = $(wildcard $(_SRCDIR_)*.h $(_SRCDIR_)lib/*.h $(_SRCDIR_)protocols/*.h)
    1414subdirs = lib protocols
  • bitlbee.conf

    r446a23e r8e6ecfe  
    5151##
    5252# AuthMode = Open
     53
     54## AuthBackend
     55##
     56## By default, the authentication data for a user is stored in the storage
     57## backend. If you want to authenticate against another authentication system
     58## (e.g. ldap), you can specify that here.
     59##
     60## Beware that this disables password changes and causes passwords for the
     61## accounts people create to be stored in plain text instead of encrypted with
     62## their bitlbee password.
     63#
     64# AuthBackend = storage
     65#
    5366
    5467## AuthPassword
  • bitlbee.h

    r446a23e r8e6ecfe  
    133133#include "irc.h"
    134134#include "storage.h"
     135#include "auth.h"
    135136#include "set.h"
    136137#include "nogaim.h"
     
    154155        conf_t *conf;
    155156        GList *storage; /* The first backend in the list will be used for saving */
     157        GList *auth;    /* Authentication backends */
    156158        char *helpfile;
    157159        int restart;
  • conf.c

    r446a23e r8e6ecfe  
    5555        conf->runmode = RUNMODE_INETD;
    5656        conf->authmode = AUTHMODE_OPEN;
     57        conf->auth_backend = NULL;
    5758        conf->auth_pass = NULL;
    5859        conf->oper_pass = NULL;
     
    241242                                        conf->authmode = AUTHMODE_OPEN;
    242243                                }
     244                        } else if (g_strcasecmp(ini->key, "authbackend") == 0) {
     245                                if (g_strcasecmp(ini->value, "storage") == 0) {
     246                                        conf->auth_backend = NULL;
     247                                } else {
     248                                        fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
     249                                        return 0;
     250                                }
    243251                        } else if (g_strcasecmp(ini->key, "authpassword") == 0) {
    244252                                g_free(conf->auth_pass);
  • conf.h

    r446a23e r8e6ecfe  
    3737        runmode_t runmode;
    3838        authmode_t authmode;
     39        char *auth_backend;
    3940        char *auth_pass;
    4041        char *oper_pass;
  • configure

    r446a23e r8e6ecfe  
    628628done
    629629echo "STORAGE_OBJS="$STORAGE_OBJS >> Makefile.settings
     630
     631authobjs=
     632authlibs=
     633echo AUTH_OBJS=$authobjs >> Makefile.settings
     634echo EFLAGS+=$authlibs >> Makefile.settings
    630635
    631636if [ "$strip" = 0 ]; then
  • irc.h

    r446a23e r8e6ecfe  
    9292                           logging in, this may contain a password we should
    9393                           send to identify after USER/NICK are received. */
     94        char *auth_backend;
    9495
    9596        char umode[8];
  • irc_commands.c

    r446a23e r8e6ecfe  
    9797        /* just check the password here to be able to reply with useful numerics
    9898         * the actual identification will be handled later */
    99         status = storage_check_pass(user, pass);
     99        status = auth_check_pass(irc, user, pass);
    100100
    101101        if (status == STORAGE_OK) {
  • root_commands.c

    r446a23e r8e6ecfe  
    143143        }
    144144
    145         if (load) {
     145        status = auth_check_pass(irc, irc->user->nick, password);
     146        if (load && (status == STORAGE_OK)) {
    146147                status = storage_load(irc, password);
    147         } else {
    148                 status = storage_check_pass(irc->user->nick, password);
    149148        }
    150149
     
    159158                irc_rootmsg(irc, "Password accepted%s",
    160159                            load ? ", settings and accounts loaded" : "");
    161                 irc_setpass(irc, password);
    162160                irc->status |= USTATUS_IDENTIFIED;
    163161                irc_umode_set(irc, "+R", 1);
     
    268266        storage_status_t status;
    269267
    270         status = storage_remove(irc->user->nick, cmd[1]);
     268        status = auth_check_pass(irc, irc->user->nick, cmd[1]);
     269        if (status == STORAGE_OK) {
     270                status = storage_remove(irc->user->nick);
     271        }
     272
    271273        switch (status) {
    272274        case STORAGE_NO_SUCH_USER:
  • storage.c

    r446a23e r8e6ecfe  
    8787}
    8888
    89 storage_status_t storage_check_pass(const char *nick, const char *password)
     89storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password)
    9090{
    9191        GList *gl;
     
    9797                storage_status_t status;
    9898
    99                 status = st->check_pass(nick, password);
     99                status = st->check_pass(irc, nick, password);
    100100                if (status != STORAGE_NO_SUCH_USER) {
    101101                        return status;
     
    171171}
    172172
    173 storage_status_t storage_remove(const char *nick, const char *password)
     173storage_status_t storage_remove(const char *nick)
    174174{
    175175        GList *gl;
     
    185185                storage_status_t status;
    186186
    187                 status = st->remove(nick, password);
     187                status = st->remove(nick);
    188188                ok |= status == STORAGE_OK;
    189189                if (status != STORAGE_NO_SUCH_USER && status != STORAGE_OK) {
  • storage.h

    r446a23e r8e6ecfe  
    3131        STORAGE_NO_SUCH_USER,
    3232        STORAGE_INVALID_PASSWORD,
     33        STORAGE_CHECK_BACKEND,
    3334        STORAGE_ALREADY_EXISTS,
    3435        STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as
     
    4344        void (*init)(void);
    4445
    45         storage_status_t (*check_pass)(const char *nick, const char *password);
     46        storage_status_t (*check_pass)(irc_t *irc, const char *nick, const char *password);
    4647
    4748        storage_status_t (*load)(irc_t *irc, const char *password);
    4849        storage_status_t (*save)(irc_t *irc, int overwrite);
    49         storage_status_t (*remove)(const char *nick, const char *password);
     50        storage_status_t (*remove)(const char *nick);
    5051
    5152        /* May be NULL if not supported by backend */
     
    5354} storage_t;
    5455
    55 storage_status_t storage_check_pass(const char *nick, const char *password);
     56storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password);
    5657
    5758storage_status_t storage_load(irc_t * irc, const char *password);
    5859storage_status_t storage_save(irc_t *irc, char *password, int overwrite);
    59 storage_status_t storage_remove(const char *nick, const char *password);
     60storage_status_t storage_remove(const char *nick);
    6061
    6162void register_storage_backend(storage_t *);
  • storage_xml.c

    r446a23e r8e6ecfe  
    3434
    3535typedef enum {
    36         XML_PASS_CHECK_ONLY = -1,
    37         XML_PASS_UNKNOWN = 0,
    38         XML_PASS_WRONG,
    39         XML_PASS_OK
    40 } xml_pass_st;
     36        XML_PASS_CHECK = 0,
     37        XML_LOAD
     38} xml_action;
    4139
    4240/* To make it easier later when extending the format: */
     
    121119        if (!handle || !pass_b64 || !protocol || !prpl) {
    122120                return XT_ABORT;
    123         } else if ((pass_len = base64_decode(pass_b64, (unsigned char **) &pass_cr)) &&
    124                    arc_decode(pass_cr, pass_len, &password, xd->given_pass) >= 0) {
    125                 acc = account_add(xd->irc->b, prpl, handle, password);
    126                 if (server) {
    127                         set_setstr(&acc->set, "server", server);
    128                 }
    129                 if (autoconnect) {
    130                         set_setstr(&acc->set, "auto_connect", autoconnect);
    131                 }
    132                 if (tag) {
    133                         set_setstr(&acc->set, "tag", tag);
    134                 }
    135                 if (local) {
    136                         acc->flags |= ACC_FLAG_LOCAL;
    137                 }
    138                 if (locked && !g_strcasecmp(locked, "true")) {
    139                         acc->flags |= ACC_FLAG_LOCKED;
    140                 }
     121        }
     122
     123        base64_decode(pass_b64, (unsigned char **) &pass_cr);
     124        if (xd->irc->auth_backend) {
     125                password = g_strdup((char *)pass_cr);
    141126        } else {
    142                 g_free(pass_cr);
    143                 g_free(password);
    144                 return XT_ABORT;
     127                pass_len = arc_decode(pass_cr, pass_len, &password, xd->given_pass);
     128                if (pass_len < 0) {
     129                        g_free(pass_cr);
     130                        g_free(password);
     131                        return XT_ABORT;
     132                }
     133        }
     134
     135        acc = account_add(xd->irc->b, prpl, handle, password);
     136        if (server) {
     137                set_setstr(&acc->set, "server", server);
     138        }
     139        if (autoconnect) {
     140                set_setstr(&acc->set, "auto_connect", autoconnect);
     141        }
     142        if (tag) {
     143                set_setstr(&acc->set, "tag", tag);
     144        }
     145        if (local) {
     146                acc->flags |= ACC_FLAG_LOCAL;
     147        }
     148        if (locked && !g_strcasecmp(locked, "true")) {
     149                acc->flags |= ACC_FLAG_LOCKED;
    145150        }
    146151
     
    198203};
    199204
    200 static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_pass_st action)
     205static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_action action)
    201206{
    202207        struct xml_parsedata xd[1];
     
    240245        }
    241246
    242         {
     247        if (action == XML_PASS_CHECK) {
    243248                char *nick = xt_find_attr(node, "nick");
    244249                char *pass = xt_find_attr(node, "password");
    245 
    246                 if (!nick || !pass) {
     250                char *backend = xt_find_attr(node, "auth_backend");
     251
     252                if (!nick || !(pass || backend)) {
    247253                        goto error;
     254                }
     255
     256                if (backend) {
     257                        g_free(xd->irc->auth_backend);
     258                        xd->irc->auth_backend = g_strdup(backend);
     259                        ret = STORAGE_CHECK_BACKEND;
    248260                } else if ((st = md5_verify_password(xd->given_pass, pass)) != 0) {
    249261                        ret = STORAGE_INVALID_PASSWORD;
    250                         goto error;
    251                 }
    252         }
    253 
    254         if (action == XML_PASS_CHECK_ONLY) {
    255                 ret = STORAGE_OK;
    256                 goto error;
    257         }
    258 
    259         /* DO NOT call xt_handle() before verifying the password! */
     262                } else {
     263                        ret = STORAGE_OK;
     264                }
     265                goto error;
     266        }
     267
    260268        if (xt_handle(xp, NULL, 1) == XT_HANDLED) {
    261269                ret = STORAGE_OK;
     
    272280static storage_status_t xml_load(irc_t *irc, const char *password)
    273281{
    274         return xml_load_real(irc, irc->user->nick, password, XML_PASS_UNKNOWN);
    275 }
    276 
    277 static storage_status_t xml_check_pass(const char *my_nick, const char *password)
    278 {
    279         return xml_load_real(NULL, my_nick, password, XML_PASS_CHECK_ONLY);
     282        return xml_load_real(irc, irc->user->nick, password, XML_LOAD);
     283}
     284
     285static storage_status_t xml_check_pass(irc_t *irc, const char *my_nick, const char *password)
     286{
     287        return xml_load_real(irc, my_nick, password, XML_PASS_CHECK);
    280288}
    281289
     
    292300        struct xt_node *root, *cur;
    293301
    294         /* Generate a salted md5sum of the password. Use 5 bytes for the salt
    295            (to prevent dictionary lookups of passwords) to end up with a 21-
    296            byte password hash, more convenient for base64 encoding. */
    297         random_bytes(pass_md5 + 16, 5);
    298         md5_init(&md5_state);
    299         md5_append(&md5_state, (md5_byte_t *) irc->password, strlen(irc->password));
    300         md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
    301         md5_finish(&md5_state, pass_md5);
    302         /* Save the hash in base64-encoded form. */
    303         pass_buf = base64_encode(pass_md5, 21);
    304 
    305302        root = cur = xt_new_node("user", NULL, NULL);
     303        if (irc->auth_backend) {
     304                xt_add_attr(cur, "auth_backend", irc->auth_backend);
     305        } else {
     306                /* Generate a salted md5sum of the password. Use 5 bytes for the salt
     307                   (to prevent dictionary lookups of passwords) to end up with a 21-
     308                   byte password hash, more convenient for base64 encoding. */
     309                random_bytes(pass_md5 + 16, 5);
     310                md5_init(&md5_state);
     311                md5_append(&md5_state, (md5_byte_t *) irc->password, strlen(irc->password));
     312                md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
     313                md5_finish(&md5_state, pass_md5);
     314                /* Save the hash in base64-encoded form. */
     315                pass_buf = base64_encode(pass_md5, 21);
     316                xt_add_attr(cur, "password", pass_buf);
     317                g_free(pass_buf);
     318        }
     319
    306320        xt_add_attr(cur, "nick", irc->user->nick);
    307         xt_add_attr(cur, "password", pass_buf);
    308321        xt_add_attr(cur, "version", XML_FORMAT_VERSION);
    309 
    310         g_free(pass_buf);
    311322
    312323        xml_generate_settings(cur, &irc->b->set);
     
    319330                int pass_len;
    320331
    321                 pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
    322                 pass_b64 = base64_encode(pass_cr, pass_len);
    323                 g_free(pass_cr);
     332                if(irc->auth_backend) {
     333                        /* If we don't "own" the password, it may change without us
     334                         * knowing, so we cannot encrypt the data, as we then may not be
     335                         * able to decrypt it */
     336                        pass_b64 = base64_encode((unsigned char *)acc->pass, strlen(acc->pass));
     337                } else {
     338                        pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
     339                        pass_b64 = base64_encode(pass_cr, pass_len);
     340                        g_free(pass_cr);
     341                }
    324342
    325343                cur = xt_new_node("account", NULL, NULL);
     
    440458
    441459
    442 static storage_status_t xml_remove(const char *nick, const char *password)
     460static storage_status_t xml_remove(const char *nick)
    443461{
    444462        char s[512], *lc;
    445         storage_status_t status;
    446 
    447         status = xml_check_pass(nick, password);
    448         if (status != STORAGE_OK) {
    449                 return status;
    450         }
    451463
    452464        lc = g_strdup(nick);
  • tests/Makefile

    r446a23e r8e6ecfe  
    1515distclean: clean
    1616
    17 main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o
     17main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o auth.o
    1818
    1919test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_set.o check_jabber_sasl.o check_jabber_util.o
  • unix.c

    r446a23e r8e6ecfe  
    104104        }
    105105
     106        global.auth = auth_init(global.conf->auth_backend);
     107        if (global.conf->auth_backend && global.auth == NULL) {
     108                log_message(LOGLVL_ERROR, "Unable to load authentication backend '%s'", global.conf->auth_backend);
     109                return(1);
     110        }
     111
    106112        if (global.conf->runmode == RUNMODE_INETD) {
    107113                log_link(LOGLVL_ERROR, LOGOUTPUT_IRC);
Note: See TracChangeset for help on using the changeset viewer.