Compare commits

...

10 Commits

@ -1 +1 @@
0.0.1
0.0.2

1
src/.gitignore vendored

@ -2,3 +2,4 @@ ejabberdauth
.deps
testimap
testsmtp
testeport

@ -10,7 +10,7 @@ AM_CPPFLAGS = \
AM_CFLAGS = -Wall -g
if BUILD_TESTS
bin_PROGRAMS = ejabberdauth testimap testsmtp
bin_PROGRAMS = ejabberdauth testimap testsmtp testeport
else
bin_PROGRAMS = ejabberdauth
endif
@ -27,5 +27,9 @@ testimap_LDADD = $(LIBS) $(OPENSSL_LIBS)
testsmtp_SOURCES = tls.c smtp.c tests/misc.c tests/testsmtp.c
testsmtp_LDADD = $(LIBS) $(OPENSSL_LIBS)
testeport_SOURCES = eport.c tests/misc.c tests/testeport.c
testeport_LDADD = $(LIBS)
endif

@ -55,7 +55,7 @@ ssize_t eport_read(FILE *stream, char *buffer, size_t buffer_size)
ssize_t eport_write(FILE *stream, char *buffer, size_t buffer_size)
{
int fd = fileno(stream);
int16_t port_msg_len = (buffer_size << 8) | ((buffer_size >> 8) & 0xFF);
int16_t port_msg_len = ((int16_t)buffer_size << 8) | (((int16_t)buffer_size >> 8) & 0xFF);
ssize_t wr = 0;
struct iovec msg[2];
@ -90,7 +90,7 @@ int eport_ejabberd_msgread(char *buffer, size_t buffer_size,
if(!m) goto ieinval;
/* calc hash for first message word till the end or till the ':' */
while(((c = *buffer++) != '\0') || (*buffer == ':'))
while(((c = *buffer++) != '\0') && (c != ':'))
hash = ((hash << 5) + hash) + c;
switch(hash) {
@ -108,7 +108,7 @@ int eport_ejabberd_msgread(char *buffer, size_t buffer_size,
m->msg_data = orig_buffer;
m->user = m->domain = m->password = NULL;
/* shift a pointer */
c = *buffer++; orig_buffer = buffer;
c = *buffer; orig_buffer = buffer;
while((buffer = strchr(buffer, ':')) != NULL) {
switch(t) {

@ -42,15 +42,15 @@ int imap_auth(struct tlsport *p, struct ejabber_msg *m)
int len = 0, mlen = 0;
/* sanity check */
if(!m || !m->user) return -1;
if(!m->domain || !m->password) return -1;
if(!p || !p->ssl) return -1;
if(!m || !m->user) return 0;
if(!m->domain || !m->password) return 0;
if(!p || !p->ssl) return 0;
/* read welcome message */
memset(buf, '\0', IMAPLINE_LENGTH);
len = tls_io(p, buf, IMAPLINE_LENGTH - sizeof(char), TLSIO_READ);
if(len < 5) return -1;
else if(strncmp(buf, "* OK", 4)) return -1;
if(len < 5) return 0;
else if(strncmp(buf, "* OK", 4)) return 0;
/* check the leftovers in TLS channel */
if(len == IMAPLINE_LENGTH - sizeof(char)) {
do {
@ -62,19 +62,19 @@ int imap_auth(struct tlsport *p, struct ejabber_msg *m)
/* try to login with provided credentials */
mlen = strlen(m->user) + strlen(m->domain) + strlen(m->password);
mlen += 16; /* "a login 11 22" */
if(mlen > IMAPLINE_LENGTH - sizeof(char)) return -1; /* too long */
if(mlen > IMAPLINE_LENGTH - sizeof(char)) return 0; /* too long */
/* create a message */
snprintf(buf, mlen, "a login \"%s@%s\" \"%s\"\n", m->user, m->domain,
m->password);
len = tls_io(p, buf, mlen - sizeof(char), TLSIO_WRITE);
if(len < 0) return -1;
if(len < 0) return 0;
else memset(buf, '\0', mlen);
/* read */
len = tls_io(p, buf, IMAPLINE_LENGTH - sizeof(char), TLSIO_READ);
if(len < 5) return -1;
if(len < 5) return 0;
if(strncmp(buf, "a OK", 4)) return -1;
if(strncmp(buf, "a OK", 4)) return 0;
/* check for leftovers */
if(len == IMAPLINE_LENGTH - sizeof(char)) {
@ -88,5 +88,5 @@ int imap_auth(struct tlsport *p, struct ejabber_msg *m)
snprintf(buf, IMAPLINE_LENGTH - sizeof(char), "a logout");
len = tls_io(p, buf, 9, TLSIO_WRITE);
return 0;
return 1;
}

@ -19,7 +19,25 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <stdarg.h>
#include <getopt.h>
#include <ejabbermsg.h>
#include <tlsport.h>
#include <imapf.h>
#include <smtpf.h>
#include <eport.h>
unsigned long djb_hash(const char *s)
@ -33,8 +51,135 @@ unsigned long djb_hash(const char *s)
return hash;
}
#define MAX_SUPPL_DATA 64
#define PRG_NAME "daemon for ejabberd to provide IMAPS/SMTPS auth"
static void print_help(const char *cname)
{
fprintf(stdout, "\n%s - %s\n\n", cname, PRG_NAME);
fprintf(stdout, "USAGE:\n");
fprintf(stdout, "\t%s -S | --smtp-host <SMTP hostname> -s | --smtp-port <port> "
"-I | --imap-host <IMAP hostname> -i | --imap-port <port>", cname);
fprintf(stdout, "\n\n\t%s -h | --help\n", cname);
fprintf(stdout, "\nNOTE: This program is designed to be used from ejabberd only.\n");
return;
}
int main(int argc, char **argv)
{
char *smtp_host, *smtp_port, *imap_host, *imap_port;
char msgbuf[MAX_USER_LEN + MAX_DOMAIN_LEN + MAX_PASSWORD_LEN + MAX_SUPPL_DATA];
struct tlsport connection;
struct ejabber_msg m;
int opt, msgbuflen = 0;
smtp_host = smtp_port = imap_port = imap_host = NULL;
while(1) {
int option_index = 0;
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"smtp-host", required_argument, NULL, 'S'},
{"smtp-port", required_argument, NULL, 's'},
{"imap-host", required_argument, NULL, 'I'},
{"imap-port", required_argument, NULL, 'i'},
{NULL, 0, NULL, 0},
};
if((opt = getopt_long(argc, argv, "hS:s:I:i:", long_options, &option_index)) == -1)
break;
switch(opt) {
case 'h':
print_help(argv[0]);
return 0;
break;
case 'S': smtp_host = optarg; break;
case 's': smtp_port = optarg; break;
case 'I': imap_host = optarg; break;
case 'i': imap_port = optarg; break;
default: abort(); break;
}
}
/* arguments check up */
if(!smtp_host || !smtp_port) {
fprintf(stderr, "Error: SMTPs information is not complete.\n");
main_einval_moimoi:
print_help(argv[0]);
return EINVAL;
}
if(!imap_port || !imap_host) {
fprintf(stderr, "Error: IMAPs information is not complete.\n");
goto main_einval_moimoi;
}
/* take care about space borrowed from stack */
msgbuflen = MAX_USER_LEN + MAX_DOMAIN_LEN + MAX_PASSWORD_LEN + MAX_SUPPL_DATA;
memset(msgbuf, 0, msgbuflen);
/* libs init */
ssllib_init();
/* main loop */
while(eport_read(stdin, msgbuf, msgbuflen) > 0) {
opt = eport_ejabberd_msgread(msgbuf, msgbuflen, &m);
if(opt < 0) {
abort_parsing:
fprintf(stderr, "Error: message isn't valid.\nExiting.\n");
ssllib_free();
abort();
}
/* deal with message */
switch(ejabber_msg_event(&m)) {
case EJA_AUTH:
if(opt != 3) goto abort_parsing;
/* connect to IMAPs */
if(tls_connect(imap_host, imap_port, &connection)) {
fprintf(stderr, "Error: unable to connect to IMAPs.\n");
opt = 0;
} else {
opt = imap_auth(&connection, &m);
tls_close(&connection);
}
break;
case EJA_ISUSER:
if(opt != 2) goto abort_parsing;
/* connect to SMTPs */
if(tls_connect(smtp_host, smtp_port, &connection)) {
fprintf(stderr, "Error: unable to connect to SMTPs.\n");
opt = 0;
} else {
opt = smtp_checkuser(&connection, &m, smtp_host);
tls_close(&connection);
}
break;
case EJA_SETPASS:
case EJA_TRYREGISTER:
case EJA_REMOVEUSER:
case EJA_REMOVEUSER3:
/* still aren't supported by the program */
fprintf(stderr, "Error: This type of message are not supported.\n");
opt = 0;
break;
case EJA_UNKNOWN:
default: /* something goes wrong with message */
goto abort_parsing;
break;
}
/* finally, reply ... */
opt = ((int16_t)opt << 8) | (((int16_t)opt >> 8) & 0xff);
eport_write(stdout, (char *)(&opt), sizeof(int16_t));
/* clear the buffer */
memset(msgbuf, 0, msgbuflen);
}
ssllib_free();
return 0;
}

@ -42,62 +42,63 @@ int smtp_checkuser(struct tlsport *p, struct ejabber_msg *m, const char *host)
int len, mlen, r;
/* sanity check */
if(!m || !m->user) return -1;
if(!m->domain || !m->password) return -1;
if(!p || !p->ssl) return -1;
if(!host) return -1;
if(!m || !m->user) return 0;
if(!m->domain || !m->password) return 0;
if(!p || !p->ssl) return 0;
if(!host) return 0;
/* clear stack buffers */
len = mlen = r = 0;
len = mlen = 0;
memset(buf, 0, SMTPLINE_LENGTH);
r = 1;
/* read greetings, shall be 220 for error code */
len = tls_io(p, buf, SMTPLINE_LENGTH - sizeof(char), TLSIO_READ);
if(len < 3) return -1;
else if(strncmp(buf, "220", 3)) return -1;
if(len < 3) return 0;
else if(strncmp(buf, "220", 3)) return 0;
memset(buf, 0, len + sizeof(char));
/* start with hello */
mlen = strlen(host) + sizeof(char)*6;
if(mlen > SMTPLINE_LENGTH - sizeof(char)) return -1; /* too long message */
if(mlen > SMTPLINE_LENGTH - sizeof(char)) return 0; /* too long message */
snprintf(buf, mlen + sizeof(char), "HELO %s\n", host);
len = tls_io(p, buf, mlen, TLSIO_WRITE);
if(len < 0) return -1;
if(len < 0) return 0;
else memset(buf, 0, mlen);
/* check the reply if any */
len = tls_io(p, buf, SMTPLINE_LENGTH - sizeof(char), TLSIO_READ);
if(len < 4) return -1;
else if(strncmp(buf, "250 ", 4)) return -1;
if(len < 4) return 0;
else if(strncmp(buf, "250 ", 4)) return 0;
memset(buf, 0, len + sizeof(char));
/* set mail from */
mlen = strlen(m->user) + strlen(m->domain) + 14*sizeof(char);
if(mlen > SMTPLINE_LENGTH - sizeof(char)) return -1; /* too long message */
if(mlen > SMTPLINE_LENGTH - sizeof(char)) return 0; /* too long message */
snprintf(buf, mlen + sizeof(char), "mail from:<%s@%s>\n", m->user, m->domain);
len = tls_io(p, buf, mlen, TLSIO_WRITE);
if(len < 0) return -1;
if(len < 0) return 0;
else memset(buf, 0, mlen);
/* check the reply if any */
len = tls_io(p, buf, SMTPLINE_LENGTH - sizeof(char), TLSIO_READ);
if(len < 4) return -1;
else if(strncmp(buf, "250 ", 4)) return -1;
if(len < 4) return 0;
else if(strncmp(buf, "250 ", 4)) return 0;
memset(buf, 0, len + sizeof(char));
/* set recepient, if it's ok - user exists, if it's not - no such user */
mlen = strlen(m->user) + strlen(m->domain) + 12*sizeof(char);
if(mlen > SMTPLINE_LENGTH - sizeof(char)) return -1; /* too long message */
if(mlen > SMTPLINE_LENGTH - sizeof(char)) return 0; /* too long message */
snprintf(buf, mlen + sizeof(char), "rcpt to:<%s@%s>\n", m->user, m->domain);
len = tls_io(p, buf, mlen, TLSIO_WRITE);
if(len < 0) return -1;
if(len < 0) return 0;
else memset(buf, 0, mlen);
/* check the reply if any */
len = tls_io(p, buf, SMTPLINE_LENGTH - sizeof(char), TLSIO_READ);
if(len < 4) return -1;
else if(strncmp(buf, "250 ", 4)) r = -1;
if(len < 4) return 0;
else if(strncmp(buf, "250 ", 4)) r = 0;
/* gracefully end smtp session */
mlen = strlen("quit\n");

@ -0,0 +1,277 @@
/*
* ejabberd external authentication program
*
* (c) Alexander Vdolainen 2013, 2018, 2019, 2021 <alex@vapaa.xyz>
*
* this is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* this is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <errno.h>
#include <netdb.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <stdarg.h>
#include <getopt.h>
#include <ejabbermsg.h>
#include <eport.h>
#define PRG_NAME "ejabberauth erlang port function test"
extern int ejda_username_split(char *buf, char **user, char **domain);
/*
* Test is simple - create a pipe, write a message with eport interface
* read it, reply with erport interface.
* for supported commands replies ok, fail otherwise
*/
static void print_help(const char *cname)
{
fprintf(stdout, "\n%s - %s\n\n", cname, PRG_NAME);
fprintf(stdout, "USAGE:\n");
fprintf(stdout, "\t%s -u | --user <username>"
" -P | --password <password> [ -v | --verbose ]",
cname);
fprintf(stdout, "\n\n\t%s -h | --help\n", cname);
return;
}
const char *tfmt[] = {
"auth:%s:%s:%s",
"isuser:%s:%s",
"setpass:%s:%s:%s",
"tryregister:%s:%s:%s",
"removeuser:%s:%s",
"removeuser3:%s:%s:%s",
"weird-stuff",
};
int main(int argc, char **argv)
{
char *domain, *user, *password;
char *utbuf = NULL, *msgbuf = NULL;
int verbose = 0, opt, msgbuf_len = 0, items = 0;
FILE *inp, *outp;
int pfd[2];
struct ejabber_msg m;
domain = user = password = NULL;
inp = outp = NULL;
/* parse options */
while(1) {
int option_index = 0;
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"user", required_argument, NULL, 'u'},
{"password", required_argument, NULL, 'P'},
{"verbose", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0},
};
if((opt = getopt_long(argc, argv, "hu:P:v", long_options,
&option_index)) == -1) break;
switch(opt) {
case 'h':
/* print help information and exit */
print_help(argv[0]);
return 0;
break;
case 'u': user = optarg; break;
case 'P': password = optarg; break;
case 'v': verbose = 1; break;
default: abort(); break; /* something that's shouldn't ever happen */
}
}
if(!user) {
fprintf(stderr, "Error: no username is given.\n");
main_einval:
print_help(argv[0]);
return EINVAL;
}
if(!password) {
fprintf(stderr, "Error: no password provided.\n");
goto main_einval;
}
/* split user name */
opt = strlen(user) + sizeof(char);
if((utbuf = malloc(opt)) == NULL) {
fprintf(stderr, "Error: not enough memory\n");
return ENOMEM;
} else {
memset(utbuf, '\0', opt);
memcpy(utbuf, user, opt - sizeof(char));
if(ejda_username_split(utbuf, &user, &domain)) {
fprintf(stderr, "Error: username is invalid.\n");
free(utbuf);
return EINVAL;
}
}
/* allocate a message buffer */
msgbuf_len = strlen("removeuser3") + strlen(user) + strlen(domain) +
strlen(password) + sizeof(uint16_t) + sizeof(char)*4;
if((msgbuf = malloc(msgbuf_len)) == NULL) {
fprintf(stderr, "Error: not enough memory\n");
free(utbuf);
return ENOMEM;
} else memset(msgbuf, 0, msgbuf_len);
if(pipe(pfd) == -1) {
fprintf(stderr, "Error: cannot create pipe\n");
opt = -1;
goto finit0;
} else {
opt = -1;
inp = fdopen(pfd[0], "r"); /* port to read from */
outp = fdopen(pfd[1], "w"); /* port to write to */
if(!inp || !outp) {
fprintf(stderr, "Cannot create file stream.\n");
goto finit1;
} else opt = 0;
}
do {
/* do the message write */
switch(opt) {
case 0:
case 2:
case 3:
case 5:
snprintf(msgbuf, msgbuf_len, tfmt[opt], user, domain, password);
break;
case 1:
case 4:
snprintf(msgbuf, msgbuf_len, tfmt[opt], user, domain);
break;
case 6:
snprintf(msgbuf, msgbuf_len, tfmt[opt]);
break;
default:
opt = -1;
break;
};
if(verbose) {
fprintf(stdout, "Push (%s) ... ", msgbuf);
fflush(stdout);
}
if(eport_write(outp, msgbuf, strlen(msgbuf)) < 0) {
opt = -1;
if(verbose) fprintf(stdout, "fail\n");
else fprintf(stderr, "Error write to out port.\n");
} else {
if(verbose) fprintf(stdout, "ok\n");
/* try to read */
if(verbose) {
fprintf(stdout, "Pull ... ");
fflush(stdout);
}
if(eport_read(inp, msgbuf, msgbuf_len) < 0) {
opt = -1;
if(verbose) fprintf(stdout, "fail\n");
else fprintf(stderr, "Error on reading in port.\n");
} else if((items = eport_ejabberd_msgread(msgbuf, msgbuf_len, &m)) == -1) {
if(verbose) fprintf(stdout, "fail on parsing\n");
else fprintf(stderr, "Error on parsing data from in port.\n");
opt = -1;
}
if(opt != -1) {
if(verbose) {
fprintf(stdout, "ok, has been parsed\n");
fprintf(stdout, "Checking data ... ");
fflush(stdout);
}
/* check the data first */
switch(items) {
case 3:
if(strcmp(m.password, password)) {
opt = -1;
if(verbose) fprintf(stdout, "password data is incorrect\n");
else fprintf(stderr, "Password data is invalid.\n");
}
case 2:
if(strcmp(m.user, user)) {
opt = -1;
if(verbose) fprintf(stdout, "user data is incorrect\n");
else fprintf(stderr, "User data is invalid.\n");
}
if(strcmp(m.domain, domain)) {
opt = -1;
if(verbose) fprintf(stdout, "domain data is incorrect\n");
else fprintf(stderr, "Domain data is invalid.\n");
}
break;
case 0:
default:
if(opt != 6) opt = -1;
break;
}
if(opt != -1) { /* check command */
if(verbose) {
fprintf(stdout, "ok\n");
fprintf(stdout, "Check event ... ");
fflush(stdout);
}
switch(opt) {
case 0: if(m.aev != EJA_AUTH) opt = -1; break;
case 1: if(m.aev != EJA_ISUSER) opt = -1; break;
case 2: if(m.aev != EJA_SETPASS) opt = -1; break;
case 3: if(m.aev != EJA_TRYREGISTER) opt = -1; break;
case 4: if(m.aev != EJA_REMOVEUSER) opt = -1; break;
case 5: if(m.aev != EJA_REMOVEUSER3) opt = -1; break;
default:
opt = -1;
}
if(opt == -1) {
if(verbose) fprintf(stdout, "fail\n");
else fprintf(stderr, "Event cmd invalid.\n");
} else {
if(verbose) fprintf(stdout, "ok\n");
}
}
}
}
memset(msgbuf, 0, msgbuf_len);
if(opt != -1) opt++;
} while((opt < 6) && (opt != -1));
if(opt > 0) opt = 0;
finit1:
if(!inp) close(pfd[0]);
else fclose(inp);
if(!outp) close(pfd[1]);
else fclose(outp);
finit0:
free(utbuf);
free(msgbuf);
return opt;
}

@ -84,7 +84,7 @@ int main(int argc, char **argv)
{NULL, 0, NULL, 0},
};
if((opt = getopt_long(argc, argv, "hH:p:u:P:e:v", long_options,
if((opt = getopt_long(argc, argv, "hH:p:u:e:v", long_options,
&option_index)) == -1) break;
switch(opt) {
case 'h':

Loading…
Cancel
Save