{ "chghost", CAP_CHGHOST },
{ "setname", CAP_SETNAME },
{ "account-notify", CAP_ACCOUNTNOTIFY },
+ { "sasl", 0 },
{ 0, 0 }
};
if (strcmp(args[0], "LS") == 0) {
if (args[1] && atoi(args[1]) >= 302)
client->caps |= CAP_302;
- copy_strf(&databuf, ":%s CAP * LS :cap-notify multi-prefix extended-join away-notify invite-notify extended-monitor chghost setname account-notify\r\n", hostname);
+ copy_strf(&databuf, ":%s CAP * LS :cap-notify multi-prefix extended-join away-notify invite-notify extended-monitor chghost setname account-notify%s\r\n",
+ hostname,
+ client->umode & UMODE_SECURE ? " sasl" : "");
sendbuf(client);
if (!is_registered(client)) {
client->regstate = REG_CAPLS;
// FIXME: error 410
goto unknown;
}
+ } else if(strcmp(command, "AUTHENTICATE") == 0) {
+ if (is_registered(client))
+ return err_alreadyregistered(client), sendbuf(client), 0;
+ if (!args[0])
+ return err_needmoreparams(client, command), sendbuf(client), 0;
+ if (client->username && client->username[0] != '~')
+ return copy_strf(&databuf, ":%s 907 * :You have already authenticated using SASL\r\n", hostname), sendbuf(client), 0;
+ if (!(client->umode & UMODE_SECURE))
+ return copy_strf(&databuf, ":%s 904 * :SASL authentication failed\r\n", hostname), sendbuf(client), 0;
+ if (!client->saslbuf) {
+ if (strcmp(args[0], "PLAIN") != 0)
+ return copy_strf(&databuf, ":%s 908 * PLAIN :are available SASL mechanisms\r\n", hostname), sendbuf(client), 0;
+ copy_strf(&databuf, ":%s AUTHENTICATE +\r\n", hostname), sendbuf(client);
+ // RFC 4616 spec says the three parts of the response can be up to 255 bytes long
+ // That's 255*3 + 2, but we'll add another NUL byte after the pass, so it's 768.
+ // Base64 encoding adds 4/3 overhead, so the total buffer size is 1024.
+ client->saslbuf = malloc(1024);
+ client->saslbufLen = 0;
+ if (!client->saslbuf)
+ abort();
+ } else {
+ size_t len = strlen(args[0]);
+ // there are three cases
+ // len == 400 - copy the data, keep reading
+ // len < 400 - copy the data, finish
+ // data is + - don't copy, finish
+ if (len == 1 && args[0][0] == '*') {
+ copy_strf(&databuf, ":%s 906 * :SASL auhtentication aborted\r\n", hostname), sendbuf(client);
+ goto saslcleanup;
+ }
+ if (len != 1 || args[0][0] != '+') {
+ if (len > 400 || client->saslbufLen + len > 1024) {
+ memset(args[0], 0, len);
+ if (len > 400)
+ copy_strf(&databuf, ":%s 905 * :SASL message too long\r\n", hostname), sendbuf(client);
+ else
+ copy_strf(&databuf, ":%s 904 * :SASL authentication failed\r\n", hostname), sendbuf(client);
+ goto saslcleanup;
+ }
+ memcpy(client->saslbuf + client->saslbufLen, args[0], len);
+ client->saslbufLen += len;
+ memset(args[0], 0, len);
+ }
+ if (len != 400) {
+ while (client->saslbufLen & 3)
+ client->saslbuf[client->saslbufLen++] = '=';
+ if (client->saslbufLen == 0)
+ goto saslerr;
+ int parity = 0;
+ if (client->saslbuf[client->saslbufLen-1] == '=') {
+ client->saslbuf[client->saslbufLen-1] = 'A';
+ parity = 1;
+ if (client->saslbuf[client->saslbufLen-2] == '=') {
+ client->saslbuf[client->saslbufLen-2] = 'A';
+ parity = 2;
+ }
+ }
+ uint8_t *p, *q;
+ p = (uint8_t *)client->saslbuf;
+ for (size_t i = 0; i < client->saslbufLen; i++) {
+ if (*p >= 'A' && *p <= 'Z') {
+ *p += 0-'A';
+ } else if (*p >= 'a' && *p <= 'z') {
+ *p += 26-'a';
+ } else if (*p >= '0' && *p <= '9') {
+ *p += 52-'0';
+ } else if (*p == '+') {
+ *p = 62;
+ } else if (*p == '/') {
+ *p = 63;
+ } else {
+ goto saslerr;
+ }
+ p++;
+ }
+ p = q = (uint8_t *)client->saslbuf;
+ for (size_t i = 0; i < client->saslbufLen; i += 4) {
+ *q++ = p[0]<<2 | p[1]>>4;
+ *q++ = p[1]<<4 | p[2]>>2;
+ *q++ = p[2]<<6 | p[3];
+ p += 4;
+ }
+ client->saslbufLen = q-(uint8_t *)client->saslbuf - parity;
+ client->saslbuf[client->saslbufLen] = '\0';
+ char *username = strchr(client->saslbuf, '\0')+1;
+ if (username > client->saslbuf + client->saslbufLen)
+ goto saslerr;
+ char *password = strchr(username, '\0')+1;
+ if (password > client->saslbuf + client->saslbufLen)
+ goto saslerr;
+ printf("hello %d %s:%s\n", parity, username, password);
+ int res = password_check(username, password, getdatafile("users.txt"));
+ // we'll clear the whole buffer later, this is just in case username alloc fails
+ memset(password, 0, strlen(password));
+ if (res != -1) {
+ client->umode |= UMODE_REGISTERED;
+ free(client->username);
+ client->username = mystrdup(username);
+ if (!client->username)
+ abort();
+ copy_strf(&databuf, ":%s 903 * :SASL authentication successful\r\n", hostname), sendbuf(client);
+ copy_strf(&databuf, ":%s 900 * *!%s@%s %s :You are now logged in as %s\r\n",
+ hostname, client->username, client->hostname, client->username, client->username), sendbuf(client);
+ // TODO: send 900 in newUser after we know the nuh
+ } else {
+saslerr:
+ copy_strf(&databuf, ":%s 904 * :SASL authentication failed\r\n", hostname), sendbuf(client);
+ }
+saslcleanup:
+ memset(client->saslbuf, 0, 1024);
+ free(client->saslbuf);
+ client->saslbuf = 0;
+ client->saslbufLen = 0;
+ newUser(client);
+ return 0;
+ }
+ }
} else if(strcmp(command, "NICK") == 0) {
// User now registered
if (!args[0])
client->realname = mystrdup(args[3]);
if (!client->realname)
abort();
- client->hostname = mystrdup(client->peername && !strcmp(client->peername, "127.0.0.1") ? "localhost" : hostname);
- if (!client->hostname)
- abort();
// Send welcome
newUser(client);
} else if(strcmp(command, "PASS") == 0) {
}
void newUser(struct client_data *client) {
- if (client->regstate == REG_CAPLS || !client->nick_reg || !client->username)
+ if (client->regstate == REG_CAPLS || client->saslbuf || !client->nick_reg || !client->username)
return;
if (client->username[0] == '~' && client->pass_reg) {
int res = password_check(client->username + 1, client->pass_reg, getdatafile("users.txt"));
sendbuf(client);
rpl_visiblehost(client), sendbuf(client);
int changedacc = client->username[0] != '~' || newusername[0] != '~';
+ int loggedout = client->username[0] != '~' && newusername[0] == '~';
free(client->username);
client->username = mystrdup(newusername);
if (client->cookie != cookie)
sendbuf(client);
}
+ if (newusername[0] != '~') {
+ copy_strf(&databuf, ":%s 900 %s %s %s :You are now logged in as %s\r\n",
+ hostname, client->nick, client->nuh, client->username, client->username), sendbuf(client);
+ client->umode |= UMODE_REGISTERED;
+ } else {
+ if (loggedout)
+ copy_strf(&databuf, ":%s 901 %s %s :You are now logged out\r\n",
+ hostname, client->nick, client->nuh), sendbuf(client);
+ client->umode &= ~UMODE_REGISTERED;
+ }
}
void channel_sendtopic(struct client_data *client, struct channel *chan, int joining)
{