]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/nickserv.c
Updated SASL code to send account time stamp as well as account name upon success
[irc/evilnet/x3.git] / src / nickserv.c
index dfcb698690139d1721ae0e8297656065c5d2206a..3bbc05febd1a20be731de32ebc2afc71a78bf4fb 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
  */
 
+#include "base64.h"
 #include "chanserv.h"
 #include "conf.h"
 #include "config.h"
@@ -250,6 +251,7 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_HANDLEINFO_COOKIE_EMAIL", "Cookie: There is currently an email change cookie issued for this account" },
     { "NSMSG_HANDLEINFO_COOKIE_ALLOWAUTH", "Cookie: There is currently an allowauth cookie issued for this account" },
     { "NSMSG_HANDLEINFO_COOKIE_UNKNOWN", "Cookie: There is currently an unknown cookie issued for this account" },
+    { "NSMSG_HANDLEINFO_COOKIE_EMAIL_DATA", "Cookie: New email address: %s" },
     { "NSMSG_HANDLEINFO_INFOLINE", "Infoline: %s" },
     { "NSMSG_HANDLEINFO_FLAGS", "Flags: %s" },
     { "NSMSG_HANDLEINFO_EPITHET", "Epithet: %s" },
@@ -363,13 +365,13 @@ static const struct message_entry msgtab[] = {
     { "NSMSG_SET_FLAGS", "$bFLAGS:        $b%s" },
     { "NSMSG_SET_EMAIL", "$bEMAIL:        $b%s" },
     { "NSMSG_SET_MAXLOGINS", "$bMAXLOGINS:    $b%d" },
-    { "NSMSG_SET_ADVANCED", "$bADVANCED:      $b%s" },
+    { "NSMSG_SET_ADVANCED", "$bADVANCED:     $b%s" },
     { "NSMSG_SET_LANGUAGE", "$bLANGUAGE:     $b%s" },
     { "NSMSG_SET_LEVEL", "$bLEVEL:        $b%d" },
     { "NSMSG_SET_EPITHET", "$bEPITHET:      $b%s" },
     { "NSMSG_SET_NOTE", "$bNOTE:         $b%s"},
     { "NSMSG_SET_TITLE", "$bTITLE:        $b%s" },
-    { "NSMSG_SET_FAKEHOST", "$bFAKEHOST:    $b%s" },
+    { "NSMSG_SET_FAKEHOST", "$bFAKEHOST:     $b%s" },
 
     { "NSMSG_AUTO_OPER", "You have been auto-opered" },
     { "NSMSG_AUTO_OPER_ADMIN", "You have been auto-admined" },
@@ -976,7 +978,10 @@ generate_fakehost(struct handle_info *handle)
             for (target = handle->users; target; target = target->next_authed)
                break;
 
-            snprintf(buffer, sizeof(buffer), "%s", target->crypthost);
+            if (target)
+               snprintf(buffer, sizeof(buffer), "%s", target->crypthost);
+            else
+               strncpy(buffer, "none", sizeof(buffer));
         }
         return buffer;
     } else if (handle->fakehost[0] == '.') {
@@ -1131,7 +1136,7 @@ nickserv_register(struct userNode *user, struct userNode *settee, const char *ha
 {
     struct handle_info *hi;
     struct nick_info *ni;
-    char crypted[MD5_CRYPT_LENGTH];
+    char crypted[MD5_CRYPT_LENGTH] = "";
 
     if ((hi = dict_find(nickserv_handle_dict, handle, NULL))) {
         if(user)
@@ -1146,14 +1151,17 @@ nickserv_register(struct userNode *user, struct userNode *settee, const char *ha
         return 0;
     }
 
-    if (!is_secure_password(handle, passwd, user))
-        return 0;
+    if (passwd)
+    {
+        if (!is_secure_password(handle, passwd, user))
+            return 0;
 
-    cryptpass(passwd, crypted);
+        cryptpass(passwd, crypted);
+    }
 #ifdef WITH_LDAP
     if(nickserv_conf.ldap_enable && nickserv_conf.ldap_admin_dn) {
         int rc;
-        rc = ldap_do_add(handle, (no_auth ? NULL : crypted), NULL);
+        rc = ldap_do_add(handle, (no_auth || !passwd ? NULL : crypted), NULL);
         if(LDAP_SUCCESS != rc && LDAP_ALREADY_EXISTS != rc ) {
            if(user)
              send_message(user, nickserv, "NSMSG_LDAP_FAIL", ldap_err2string(rc));
@@ -1625,6 +1633,8 @@ static NICKSERV_FUNC(cmd_oregister)
             string_list_append(hi->masks, mask_canonicalized);
     }
 
+    argv[2] = "****";
+
     if (nickserv_conf.sync_log)
         SyncLog("REGISTER %s %s %s %s", hi->handle, hi->passwd, email ? email : "@", user->info); /* Send just @ for email if none */
     return 1;
@@ -1768,6 +1778,8 @@ static NICKSERV_FUNC(cmd_handleinfo)
         default: type = "NSMSG_HANDLEINFO_COOKIE_UNKNOWN"; break;
         }
         reply(type);
+        if (IsOper(user) && (hi->cookie->type == EMAIL_CHANGE))
+            reply("NSMSG_HANDLEINFO_COOKIE_EMAIL_DATA", hi->cookie->data);
     }
 
     if (hi->flags) {
@@ -5137,6 +5149,7 @@ nickserv_conf_read(void)
     str = database_get_data(conf_node, "default_maxlogins", RECDB_QSTRING);
     nickserv_conf.default_maxlogins = str ? strtoul(str, NULL, 0) : 2;
     str = database_get_data(conf_node, "hard_maxlogins", RECDB_QSTRING);
+    nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
     str = database_get_data(conf_node, KEY_OUNREGISTER_INACTIVE, RECDB_QSTRING);
     nickserv_conf.ounregister_inactive = str ? ParseInterval(str) : 86400*28;
     str = database_get_data(conf_node, KEY_OUNREGISTER_FLAGS, RECDB_QSTRING);
@@ -5149,7 +5162,6 @@ nickserv_conf_read(void)
         if(pos)
             nickserv_conf.ounregister_flags |= 1 << (pos - 1);
     }
-    nickserv_conf.hard_maxlogins = str ? strtoul(str, NULL, 0) : 10;
     if (!nickserv_conf.disable_nicks) {
         str = database_get_data(conf_node, "reclaim_action", RECDB_QSTRING);
         nickserv_conf.reclaim_action = str ? reclaim_action_from_string(str) : RECLAIM_NONE;
@@ -5336,6 +5348,10 @@ nickserv_reclaim(struct userNode *user, struct nick_info *ni, enum reclaim_actio
 
     assert(user);
     assert(ni);
+
+    if (IsLocal(user))
+        return;
+
     switch (action) {
     case RECLAIM_NONE:
         /* do nothing */
@@ -5416,6 +5432,39 @@ ctime(&hi->registered));
     log_module(MAIN_LOG, LOG_WARNING, "Using non-P10 code in accounts, not tested at all!");
 #endif
 
+#ifdef WITH_LDAP
+    if(!hi && nickserv_conf.ldap_enable && nickserv_conf.ldap_autocreate &&
+       (ldap_user_exists(stamp) == LDAP_SUCCESS)) {
+        int rc = 0;
+        int cont = 1;
+        char *email = NULL;
+        char *mask;
+
+        /* First attempt to get the email address from LDAP */
+        if((rc = ldap_get_user_info(stamp, &email) != LDAP_SUCCESS))
+            if(nickserv_conf.email_required)
+                cont = 0;
+
+        /* Now try to register the handle */
+        if (cont && (hi = nickserv_register(user, user, stamp, NULL, 1))) {
+            if(nickserv_conf.default_hostmask)
+                mask = "*@*";
+            else
+                mask = generate_hostmask(user, GENMASK_OMITNICK|GENMASK_NO_HIDING|GENMASK_ANY_IDENT);
+
+            if(mask) {
+                char* mask_canonicalized = canonicalize_hostmask(strdup(mask));
+                string_list_append(hi->masks, mask_canonicalized);
+            }
+
+            if(email) {
+                nickserv_set_email_addr(hi, email);
+                free(email);
+            }
+        }
+    }
+#endif
+
     if (hi) {
         if (HANDLE_FLAGGED(hi, SUSPENDED)) {
             return;
@@ -5473,10 +5522,243 @@ nickserv_define_func(const char *name, modcmd_func_t func, int min_level, int mu
     }
 }
 
+#define SDFLAG_STALE 0x01  /**< SASL session data is stale, delete on next pass. */
+
+struct SASLSession
+{
+    struct SASLSession *next;
+    struct SASLSession *prev;
+    struct server* source;
+    char *buf, *p;
+    int buflen;
+    char uid[128];
+    char mech[10];
+    char sslclifp[128];
+    int flags;
+};
+
+struct SASLSession *saslsessions = NULL;
+
+void
+sasl_delete_session(struct SASLSession *session)
+{
+    if (!session)
+        return;
+
+    if (session->buf)
+        free(session->buf);
+
+    if (session->next)
+        session->next->prev = session->prev;
+    if (session->prev)
+        session->prev->next = session->next;
+    else
+        saslsessions = session->next;
+
+    free(session);
+}
+
+void
+sasl_delete_stale(UNUSED_ARG(void *data))
+{
+    int delcount = 0;
+    int remcount = 0;
+    struct SASLSession *sess = NULL;
+    struct SASLSession *nextsess = NULL;
+
+    log_module(NS_LOG, LOG_DEBUG, "SASL: Checking for stale sessions");
+
+    for (sess = saslsessions; sess; sess = nextsess)
+    {
+        nextsess = sess->next;
+
+        if (sess->flags & SDFLAG_STALE)
+        {
+            delcount++;
+            sasl_delete_session(sess);
+        }
+        else
+        {
+            remcount++;
+            sess->flags |= SDFLAG_STALE;
+        }
+    }
+
+    if (delcount)
+        log_module(NS_LOG, LOG_DEBUG, "SASL: Deleted %d stale sessions, %d remaining", delcount, remcount);
+    if (remcount)
+        timeq_add(now + 30, sasl_delete_stale, NULL);
+}
+
+struct SASLSession*
+sasl_get_session(const char *uid)
+{
+    struct SASLSession *sess;
+
+    for (sess = saslsessions; sess; sess = sess->next)
+    {
+        if (!strncmp(sess->uid, uid, 128))
+        {
+            log_module(NS_LOG, LOG_DEBUG, "SASL: Found session for %s", sess->uid);
+            return sess;
+        }
+    }
+
+    sess = malloc(sizeof(struct SASLSession));
+    memset(sess, 0, sizeof(struct SASLSession));
+
+    strncpy(sess->uid, uid, 128);
+
+    if (!saslsessions)
+        timeq_add(now + 30, sasl_delete_stale, NULL);
+
+    if (saslsessions)
+        saslsessions->prev = sess;
+    sess->next = saslsessions;
+    saslsessions = sess;
+
+    log_module(NS_LOG, LOG_DEBUG, "SASL: Created session for %s", sess->uid);
+    return sess;
+}
+
+void
+sasl_packet(struct SASLSession *session)
+{
+    log_module(NS_LOG, LOG_DEBUG, "SASL: Got packet containing: %s", session->buf);
+
+    if (!session->mech[0])
+    {
+        log_module(NS_LOG, LOG_DEBUG, "SASL: No mechanism stored yet, using %s", session->buf);
+        if (strcmp(session->buf, "PLAIN")) {
+            irc_sasl(session->source, session->uid, "M", "PLAIN");
+            irc_sasl(session->source, session->uid, "D", "F");
+            sasl_delete_session(session);
+            return;
+        }
+
+        strncpy(session->mech, session->buf, 10);
+        irc_sasl(session->source, session->uid, "C", "+");
+    }
+    else /* We only have PLAIN at the moment so next message must be credentials */
+    {
+        char *raw;
+        size_t rawlen;
+        char *authzid = NULL;
+        char *authcid = NULL;
+        char *passwd = NULL;
+        char *r;
+        unsigned int i = 0, c = 0;
+        struct handle_info *hi;
+        static char buffer[256];
+
+        base64_decode_alloc(session->buf, session->buflen, &raw, &rawlen);
+
+        raw = (char *)realloc(raw, rawlen+1);
+       raw[rawlen] = '\0';
+
+        authzid = raw;
+        r = raw;
+        for (i=0; i<rawlen; i++)
+        {
+            if (!*r++)
+            {
+                if (c++)
+                    passwd = r;
+                else
+                    authcid = r;
+            }
+        }
+
+        log_module(NS_LOG, LOG_DEBUG, "SASL: Checking supplied credentials");
+
+        if (c != 2)
+        {
+            log_module(NS_LOG, LOG_DEBUG, "SASL: Incomplete credentials supplied");
+            irc_sasl(session->source, session->uid, "D", "F");
+        }
+        else
+        {
+            if (!(hi = loc_auth(NULL, authcid, passwd, NULL)))
+            {
+                log_module(NS_LOG, LOG_DEBUG, "SASL: Invalid credentials supplied");
+                irc_sasl(session->source, session->uid, "D", "F");
+            }
+            else
+            {
+                snprintf(buffer, sizeof(buffer), "%s "FMT_TIME_T, hi->handle, hi->registered);
+                log_module(NS_LOG, LOG_DEBUG, "SASL: Valid credentials supplied");
+                irc_sasl(session->source, session->uid, "L", buffer);
+                irc_sasl(session->source, session->uid, "D", "S");
+            }
+        }
+
+        sasl_delete_session(session);
+
+        free(raw);
+        return;
+    }
+
+    /* clear stale state */
+    session->flags &= ~SDFLAG_STALE;
+}
+
+void
+handle_sasl_input(struct server* source ,const char *uid, const char *subcmd, const char *data, UNUSED_ARG(const char *ext), UNUSED_ARG(void *extra))
+{
+    struct SASLSession* sess = sasl_get_session(uid);
+    int len = strlen(data);
+
+    sess->source = source;
+
+    if (!strcmp(subcmd, "D"))
+    {
+        sasl_delete_session(sess);
+        return;
+    }
+
+    if (strcmp(subcmd, "S") && strcmp(subcmd, "C"))
+        return;
+
+    if (len == 0)
+        return;
+
+    if (sess->p == NULL)
+    {
+        sess->buf = (char *)malloc(len + 1);
+        sess->p = sess->buf;
+        sess->buflen = len;
+    }
+    else
+    {
+        if (sess->buflen + len + 1 > 8192) /* This is a little much... */
+        {
+            irc_sasl(source, uid, "D", "F");
+            sasl_delete_session(sess);
+            return;
+        }
+
+        sess->buf = (char *)realloc(sess->buf, sess->buflen + len + 1);
+        sess->p = sess->buf + sess->buflen;
+        sess->buflen += len;
+    }
+
+    memcpy(sess->p, data, len);
+
+    /* Messages not exactly 400 bytes are the end of a packet. */
+    if(len < 400)
+    {
+        sasl_packet(sess);
+        sess->buflen = 0;
+        free(sess->buf);
+        sess->buf = sess->p = NULL;
+    }
+}
+
 static void
 nickserv_db_cleanup(UNUSED_ARG(void* extra))
 {
     unreg_del_user_func(nickserv_remove_user, NULL);
+    unreg_sasl_input_func(handle_sasl_input, NULL);
     userList_clean(&curr_helpers);
     policer_params_delete(nickserv_conf.auth_policer_params);
     dict_delete(nickserv_handle_dict);
@@ -5551,6 +5833,7 @@ init_nickserv(const char *nick)
     reg_del_user_func(nickserv_remove_user, NULL);
     reg_account_func(handle_account);
     reg_auth_func(handle_loc_auth_oper, NULL);
+    reg_sasl_input_func(handle_sasl_input, NULL);
 
     /* set up handle_inverse_flags */
     memset(handle_inverse_flags, 0, sizeof(handle_inverse_flags));
@@ -5680,6 +5963,7 @@ init_nickserv(const char *nick)
             AddChannelUser(nickserv, chan)->modes |= MODE_CHANOP;
         }
     }
+
 #ifdef WITH_LDAP
     ldap_do_init(nickserv_conf);
 #endif