[stunnel-users] Patch for XMPP in client mode
Philipp Hartwig
philipp.hartwig at uni-due.de
Fri May 6 00:47:30 CEST 2011
I've attached a new version which adds some sanity checks and improves
the XML "parsing".
Also the "\r\n" appended by fdputline to the starttls command actually
violated the XMPP specification[1], Section 5.3.3:
> During STARTTLS negotiation, the entities MUST NOT send any whitespace
> as separators between XML elements[...]
I have therefore replaced the fdputline with a write_blocking (although
the servers seemed happy with it).
Let me explain the assumptions I'm operating under:
After connecting to the server the client sends
> <?xml version='1.0'?><stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>
where %s is replaced by the hostname of the server. The client then
reads the response of the server, discarding all whitespace, until it
finds
> </stream:features>
or
> <stream:features/>
It aborts with an error message in the second case because the server
must advertise starttls in the features section, see Section 5.4.1 of
the spec:
> The receiving entity then MUST send stream features to the initiating
> entity. If the receiving entity supports TLS, the stream features MUST
> include an advertisement for support of STARTTLS negotiation[...]
It then sends
> <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
Section 5.4.2.1 of the spec states
> The receiving entity MUST reply with either a <proceed/> element
> (proceed case) or a <failure/> element (failure case) qualified by the
> 'urn:ietf:params:xml:ns:xmpp-tls' namespace.
so the client continues to read the server stream, discarding all whitespace and
replacing " by ', until it finds
> <proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'
or
> <failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'
In the second case it aborts with an error message, while in the first
case it continues to read until it finds
> </proceed>
or
> />
It then starts the TLS negotiation in accordance with 5.4.2.3 of the
spec:
> The receiving entity MUST consider the TLS negotiation to have begun
> immediately after sending the closing '>' character of the <proceed/>
> element to the initiating entity. The initiating entity MUST consider
> the TLS negotiation to have begun immediately after receiving the
> closing '>' character of the <proceed/> element from the receiving
> entity.
Granted this is a very naive way of "parsing" XML but it is working with
every server I've tried. Any comments?
Regards,
Philipp
[1] http://xmpp.org/rfcs/rfc6120.html
-------------- next part --------------
diff -u Desktop/stunnel-4.36/src/network.c Desktop/stunnel-4.36-new/src/network.c
--- Desktop/stunnel-4.36/src/network.c 2011-05-01 23:28:59.000000000 +0200
+++ Desktop/stunnel-4.36-new/src/network.c 2011-05-05 23:51:33.453643619 +0200
@@ -727,6 +727,64 @@
return line;
}
+/* shifts "str" to the left by 1, discarding the first character and appends "chr" as the last character */
+static void shiftstring(char *str, char chr) {
+ int i;
+ int length;
+ length=strlen(str);
+ for(i=0;i<length-1; i++){
+ str[i]=str[i+1];
+ }
+ str[length-1]=chr;
+}
+
+int read_until_alternatives(CLI *c, int fd, char *goalstring1, char *goalstring2) {
+ s_poll_set fds;
+ char line[2];
+ char string[256];
+ int length;
+ int i;
+ length=strlen(goalstring1);
+ for(i=0; i<length; i++){
+ string[i]='0';
+ }
+ string[length]='\0';
+ do {
+ s_poll_init(&fds);
+ s_poll_add(&fds, fd, 1, 0); /* read */
+ switch(s_poll_wait(&fds, c->opt->timeout_busy, 0)) {
+ case -1:
+ sockerror("read_until: s_poll_wait");
+ longjmp(c->err, 1); /* error */
+ case 0:
+ s_log(LOG_INFO, "read_until: s_poll_wait:"
+ " TIMEOUTbusy exceeded: sending reset");
+ longjmp(c->err, 1); /* timeout */
+ case 1:
+ break; /* OK */
+ default:
+ s_log(LOG_ERR, "read_until: s_poll_wait: unknown result");
+ longjmp(c->err, 1); /* error */
+ }
+ switch(readsocket(fd, line, 1)) {
+ case -1: /* error */
+ sockerror("readsocket (read_until)");
+ longjmp(c->err, 1);
+ case 0: /* EOF */
+ s_log(LOG_ERR, "Unexpected socket close (read_until)");
+ longjmp(c->err, 1);
+ }
+ if(line[0] == '"')
+ line[0]='\'';
+ if(!isspace(line[0]))
+ shiftstring(string,line[0]);
+ } while(strcmp(string, goalstring1)!=0 && !strstr(string, goalstring2));
+ if(strcmp(string, goalstring1)==0)
+ return 0;
+ else
+ return 1;
+}
+
int fdprintf(CLI *c, int fd, const char *format, ...) {
va_list ap;
char *line;
diff -u Desktop/stunnel-4.36/src/protocol.c Desktop/stunnel-4.36-new/src/protocol.c
--- Desktop/stunnel-4.36/src/protocol.c 2011-05-01 23:36:56.000000000 +0200
+++ Desktop/stunnel-4.36-new/src/protocol.c 2011-05-05 23:35:13.080782201 +0200
@@ -56,6 +56,7 @@
static void nntp_client(CLI *);
static void connect_client(CLI *);
static void ntlm(CLI *);
+static void xmpp_client(CLI *);
#ifndef OPENSSL_NO_MD4
static char *ntlm1();
static char *ntlm3(char *, char *, char *);
@@ -79,6 +80,8 @@
pop3_client(c);
else if(!strcmp(c->opt->protocol, "imap"))
imap_client(c);
+ else if(!strcmp(c->opt->protocol, "xmpp"))
+ xmpp_client(c);
else if(!strcmp(c->opt->protocol, "nntp"))
nntp_client(c);
else if(!strcmp(c->opt->protocol, "connect"))
@@ -180,6 +183,28 @@
write_blocking(c, c->local_wfd.fd, ssl_ok, sizeof ssl_ok);
}
+static void xmpp_client(CLI *c) {
+ char hello_end1[]="</stream:features>";
+ char hello_end2[]="<stream:features/>";
+ char tls_success[]="<proceedxmlns='urn:ietf:params:xml:ns:xmpp-tls'";
+ char tls_failure[]="<failurexmlns='urn:ietf:params:xml:ns:xmpp-tls'";
+ char tls_success_end1[]="</proceed>";
+ char tls_success_end2[]="/>";
+ char starttls_command[]="<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
+ fdprintf(c, c->remote_fd.fd, "<?xml version='1.0'?><stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>",strtok(c->opt->remote_address,":"));
+ if(read_until_alternatives(c, c->remote_fd.fd, hello_end1, hello_end2)) {
+ s_log(LOG_ERR, "XMPP server didn't advertise any features");
+ longjmp(c->err, 1);
+ }
+ write_blocking(c, c->remote_fd.fd, starttls_command, strlen(starttls_command));
+ s_log(LOG_DEBUG, " -> %s", starttls_command);
+ if(read_until_alternatives(c, c->remote_fd.fd, tls_success,tls_failure)) {
+ s_log(LOG_ERR, "XMPP server rejected starttls");
+ longjmp(c->err, 1);
+ }
+ read_until_alternatives(c, c->remote_fd.fd, tls_success_end1,tls_success_end2);
+}
+
static void smtp_client(CLI *c) {
char *line;
diff -u Desktop/stunnel-4.36/src/prototypes.h Desktop/stunnel-4.36-new/src/prototypes.h
--- Desktop/stunnel-4.36/src/prototypes.h 2011-05-01 20:18:01.000000000 +0200
+++ Desktop/stunnel-4.36-new/src/prototypes.h 2011-05-05 23:54:14.770443545 +0200
@@ -363,6 +363,7 @@
void read_blocking(CLI *, int fd, void *, int);
void fdputline(CLI *, int, const char *);
char *fdgetline(CLI *, int);
+int read_until_alternatives(CLI *, int, char *, char *);
/* descriptor versions of fprintf/fscanf */
int fdprintf(CLI *, int, const char *, ...)
#ifdef __GNUC__
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://www.stunnel.org/pipermail/stunnel-users/attachments/20110506/5a30ac41/attachment.sig>
More information about the stunnel-users
mailing list