]> PatchyGit - patchyircd.git/commitdiff
add SASL EXTERNAL support
authorEgor <egor@patchysicp.xyz>
Thu, 25 Jan 2024 02:22:32 +0000 (04:22 +0200)
committerEgor <egor@patchysicp.xyz>
Thu, 25 Jan 2024 02:22:32 +0000 (04:22 +0200)
data/.gitignore
src/common.h
src/irc.c
src/passwd.c
src/passwd.h

index d21035f..63bc720 100644 (file)
@@ -7,3 +7,4 @@ users.txt
 newusers.txt
 help.txt
 motd.txt
+certfp.txt
index 33029c3..af8b8cd 100644 (file)
@@ -71,6 +71,7 @@ struct client_data {
        unsigned dying :1;
        unsigned write_error :1;
        unsigned poll_reg :2;
+       unsigned saslexternal :1;
 };
 struct channel {
        char *key;
index 7991c48..25ba04e 100644 (file)
--- a/src/irc.c
+++ b/src/irc.c
@@ -216,7 +216,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                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%s\r\n",
                                        hostname,
-                                       client->umode & UMODE_SECURE ? (client->caps & CAP_302 ? " sasl=PLAIN" : " sasl") : "");
+                                       client->umode & UMODE_SECURE ? (client->caps & CAP_302 ? " sasl=PLAIN,EXTERNAL" : " sasl") : "");
                        sendbuf(client);
                        if (!is_registered(client)) {
                                client->regstate = REG_CAPLS;
@@ -276,14 +276,15 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                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;
+                       if (strcmp(args[0], "PLAIN") != 0 && strcmp(args[0], "EXTERNAL") != 0)
+                               return copy_strf(&databuf, ":%s 908 * PLAIN,EXTERNAL :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;
+                       client->saslexternal = !strcmp(args[0], "EXTERNAL");
                        if (!client->saslbuf)
                                abort();
                } else {
@@ -310,56 +311,66 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                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;
+                               if (client->saslbufLen != 0) {
+                                       while (client->saslbufLen & 3)
+                                               client->saslbuf[client->saslbufLen++] = '=';
+                                       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;
+                                               }
+                                       }
+                                       unsigned char *p, *q;
+                                       p = (unsigned char *)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 = (unsigned char *)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-(unsigned char *)client->saslbuf - parity;
                                }
-                               unsigned char *p, *q;
-                               p = (unsigned char *)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 {
+                               client->saslbuf[client->saslbufLen] = '\0';
+                               const char *username;
+                               int res = -1;
+                               if (!client->saslexternal) {
+                                       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;
+                                       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));
+                               } else {
+#ifdef HAS_TLS
+                                       if (client->cctx && tls_peer_cert_provided(client->cctx)) {
+                                               username = db_read_rhs(tls_peer_cert_hash(client->cctx), getdatafile("certfp.txt"));
+                                               res = -!username;
                                        }
-                                       p++;
-                               }
-                               p = q = (unsigned char *)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;
+#endif
                                }
-                               client->saslbufLen = q-(unsigned char *)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);
index e0d1c62..fadf685 100644 (file)
@@ -107,3 +107,26 @@ int password_check(const char *key, const char *pw, const char *fn)
        return -1;
 }
 #endif
+const char *db_read_rhs(const char *key, const char *fn) {
+       FILE *fi = fopen(fn, "rb");
+       if (!fi)
+               return 0;
+       int keylen = strlen(key);
+       static char buf[1024];
+       while (fgets(buf, 1024, fi)) {
+               char *rhs = strchr(buf, ':');
+               if (rhs) {
+                       rhs++;
+                       int len = strlen(rhs);
+                       if (rhs[len-1] == '\n')
+                               rhs[--len] = '\0';
+                       if (keylen == len && memcmp(rhs, key, keylen) == 0) {
+                               rhs[-1] = '\0';
+                               fclose(fi);
+                               return buf;
+                       }
+               }
+       }
+       fclose(fi);
+       return 0;
+}
index d33f5aa..61e00b3 100644 (file)
@@ -3,4 +3,5 @@
 const char *password_hash(const char *pw);
 int password_set(const char *key, const char *pw, const char *fn, const char *tmpfn);
 int password_check(const char *key, const char *pw, const char *fn);
+const char *db_read_rhs(const char *key, const char *fn);
 #endif