fprintf(stderr, "\n");
return irc_process_line(client, source, commandsv, argc, args);
}
-static void cont_webirc(Client *client, int res) {
- if (!(client->umode & UMODE_SECURE) || res != 0) {
- free(client->webircpeername);
- client->webircpeername = 0;
- err_passwdmismatch(client), sendbuf(client);
- } else {
- free(client->peername);
- client->peername = client->webircpeername;
- if (client->webirchostname) {
- client_chghost(client, 0, client->webirchostname);
- free(client->webirchostname);
- client->webirchostname = 0;
- }
- client->webircpeername = 0;
- if (client->webircwait == 2) {
- // NOTE: this relies on the job system being FIFO
- client->umode &= ~UMODE_SECURE;
- }
- client->webircwait = 0;
- newUser(client);
- }
-}
-static void cont_oper(Client *client, int res) {
- if (!(client->umode & UMODE_SECURE) || res != 0) {
- err_passwdmismatch(client), sendbuf(client);
- } else {
- client->umode |= UMODE_OPER;
- rpl_youreoper(client), sendbuf(client);
- copy_strf(&databuf, ":%s MODE %s +o\r\n", hostname, client->nick);
- sendbuf(client);
- }
-}
-static void cont_sasl(Client *client, int res) {
- if (client->saslusername) {
- if (res == 0) {
- rpl_saslsuccess(), sendbuf(client);
- client_chghost(client, client->saslusername, 0);
- } else {
- err_saslfail(), sendbuf(client);
- }
- free(client->saslusername);
- client->saslusername = 0;
- newUser(client);
- }
-}
-static void cont_pass(Client *client, int res) {
- if (client->passwait) {
- client->passwait = 0;
- if (!(client->umode & UMODE_SECURE) || res != 0) {
- err_passwdmismatch(client), sendbuf(client);
- } else {
- client_chghost(client, client->username+1, 0);
- }
- newUser(client);
- }
-}
#define USE_COMMAND_HASH
#ifdef USE_COMMAND_HASH
constexpr size_t command_hash(std::string_view sv) {
char *password = strchr(p, '\0')+1;
if (password > client->saslbuf + client->saslbufLen)
goto saslerr;
- job_password_check(client, username, password, "users", cont_sasl);
+ job_password_check(client, username, password, "users", [client](int res) {
+ if (client->saslusername) {
+ if (res == 0) {
+ rpl_saslsuccess(), sendbuf(client);
+ client_chghost(client, client->saslusername, 0);
+ } else {
+ err_saslfail(), sendbuf(client);
+ }
+ free(client->saslusername);
+ client->saslusername = 0;
+ newUser(client);
+ }
+ });
// we'll clear the whole buffer later, this is just in case username alloc fails
memset(password, 0, strlen(password));
client->saslusername = mystrdup(username);
memcpy(client->webirchostname+len, ".xmpp", sizeof(".xmpp"));
}
}
- job_password_check(client, args[1], args[0], "webirc", cont_webirc);
+ job_password_check(client, args[1], args[0], "webirc", [client](int res) {
+ if (!(client->umode & UMODE_SECURE) || res != 0) {
+ free(client->webircpeername);
+ client->webircpeername = 0;
+ err_passwdmismatch(client), sendbuf(client);
+ } else {
+ free(client->peername);
+ client->peername = client->webircpeername;
+ if (client->webirchostname) {
+ client_chghost(client, 0, client->webirchostname);
+ free(client->webirchostname);
+ client->webirchostname = 0;
+ }
+ client->webircpeername = 0;
+ if (client->webircwait == 2) {
+ // NOTE: this relies on the job system being FIFO
+ client->umode &= ~UMODE_SECURE;
+ }
+ client->webircwait = 0;
+ newUser(client);
+ }
+ });
memset(args[0], 0, strlen(args[0]));
}
} MATCH_COMMAND("OPER") {
if (argc < 2)
return err_needmoreparams(client, command), sendbuf(client), 0;
stringlower(args[0]);
- job_password_check(client, args[0], args[1], "ops", cont_oper);
+ job_password_check(client, args[0], args[1], "ops", [client](int res) {
+ if (!(client->umode & UMODE_SECURE) || res != 0) {
+ err_passwdmismatch(client), sendbuf(client);
+ } else {
+ client->umode |= UMODE_OPER;
+ rpl_youreoper(client), sendbuf(client);
+ copy_strf(&databuf, ":%s MODE %s +o\r\n", hostname, client->nick);
+ sendbuf(client);
+ }
+ });
memset(args[1], 0, strlen(args[1]));
} MATCH_COMMAND("NEWACC") {
if (!is_registered(client))
}
}
if (client->username[0] == '~' && client->pass_reg) {
- job_password_check(client, client->username+1, client->pass_reg, "users", cont_pass);
+ job_password_check(client, client->username+1, client->pass_reg, "users", [client](int res) {
+ if (client->passwait) {
+ client->passwait = 0;
+ if (!(client->umode & UMODE_SECURE) || res != 0) {
+ err_passwdmismatch(client), sendbuf(client);
+ } else {
+ client_chghost(client, client->username+1, 0);
+ }
+ newUser(client);
+ }
+ });
client->passwait = 1;
memset(client->pass_reg, 0, strlen(client->pass_reg));
free(client->pass_reg);
-// Copyright (c) 2024-2025 Egor
+// Copyright (c) 2024-2026 Egor
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common.hpp"
#include "job.hpp"
Client *client;
jobproc_t job;
contproc_t then;
- char *data;
- size_t len;
};
static std::deque<jobent> jobq;
if (!jobq.empty()) {
jobent &job = jobq.front();
if (!job.job && current_child == -1) {
- job.then(job.client, current_result);
+ job.then(current_result);
jobq.pop_front();
}
}
if (pid == -1) {
abortf("job_process: fork failed\n");
} else if (pid == 0) {
- int rv = job.job(job.client, job.data, job.len);
- memset(job.data, 0, job.len);
+ int rv = job.job();
_exit(rv);
} else {
current_child = pid;
- memset(job.data, 0, job.len);
- free(job.data);
- job.data = 0;
- job.len = 0;
- job.job = 0;
+ job.job = nullptr;
}
}
}
}
-void job_add(Client *cl, jobproc_t job, contproc_t then, char *data, size_t len)
+void job_add(Client *cl, jobproc_t&& job, contproc_t&& then)
{
- if (!job)
- return;
- struct jobent ent = { cl, job, then, data, len };
- jobq.push_back(ent);
+ jobq.push_back(jobent{cl, std::move(job), std::move(then)});
}
void job_kill(Client *cl)
{
auto q = p;
while (p != jobq.end()) {
if (p->client != cl) {
- *q++ = *p++;
+ *q++ = std::move(*p++);
} else {
- memset(p->data, 0, p->len);
- free(p->data);
p++;
}
}
jobq.erase(q, jobq.end());
}
-
-static int job_password_check_job(Client *cl, const char *data, size_t len)
-{
- (void)cl;
- if (!len)
- return 1;
- const char *pw = data;
- const char *salt = strchr(pw, '\0')+1;
- char *hash = password_crypt(pw, salt);
- int rv = !hash || strcmp(hash, salt) != 0;
- if (hash)
- memset(hash, 0, strlen(hash));
- return rv;
-}
-void job_password_check(Client *client, const char *key, const char *pw, const char *fn, contproc_t then)
+// FIXME: make a better version of this and use it throughout the codebase
+struct SecureStringWrapper {
+ char *data;
+ operator char*() { return data; }
+ SecureStringWrapper(const char *s) :data(nullptr) {
+ if (s) {
+ size_t len = strlen(s);
+ data = new char[len+1];
+ memcpy(data, s, len);
+ data[len] = '\0';
+ }
+ }
+ SecureStringWrapper(const SecureStringWrapper &) = delete;
+ SecureStringWrapper& operator=(const SecureStringWrapper &) = delete;
+ SecureStringWrapper(SecureStringWrapper &&other) {
+ data = std::exchange(other.data, nullptr);
+ }
+ SecureStringWrapper& operator=(SecureStringWrapper &&other) {
+ std::swap(data, other.data);
+ return *this;
+ }
+ ~SecureStringWrapper() {
+ if (data) {
+ memset(data, 0, strlen(data));
+ delete[] data;
+ }
+ }
+};
+void job_password_check(Client *client, const char *key, const char *pw, const char *fn, contproc_t&& then)
{
char *salt = password_get(key, fn);
- if (!salt) {
- job_add(client, job_password_check_job, then, 0, 0);
- } else {
- size_t pwlen = strlen(pw)+1;
- size_t len = pwlen+strlen(salt)+1;
- char *buf = (char *)malloc(len);
- if (!buf)
- abortf("job_password_check: allocation failure\n");
- memcpy(buf, pw, pwlen);
- memcpy(buf+pwlen, salt, len-pwlen);
- job_add(client, job_password_check_job, then, buf, len);
- memset(salt, 0, len-pwlen);
- }
+ // FIXME: make_shared only really needed here cuz std::function wants a copyable object
+ // C++23 adds a std::move_only_function
+ job_add(client, [pw=std::make_shared<SecureStringWrapper>(pw), salt=std::make_shared<SecureStringWrapper>(salt)]() -> int {
+ if (!*salt)
+ return 1;
+ char *hash = password_crypt(*pw, *salt);
+ int rv = !hash || strcmp(hash, *salt) != 0;
+ if (hash)
+ memset(hash, 0, strlen(hash));
+ return rv;
+ }, std::move(then));
}