a6a6734f7db004042010f6cb506d48267e1defd5
[citadel.git] / citadel / modules / xmpp / xmpp_sasl_service.c
1 /*
2  * Barebones SASL authentication service for XMPP (Jabber) clients.
3  *
4  * Note: RFC3920 says we "must" support DIGEST-MD5 but we only support PLAIN.
5  *
6  * Copyright (c) 2007-2018 by Art Cancro
7  *
8  * This program is open source software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 3.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16
17 #include "sysdep.h"
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <pwd.h>
24 #include <errno.h>
25 #include <sys/types.h>
26
27 #if TIME_WITH_SYS_TIME
28 # include <sys/time.h>
29 # include <time.h>
30 #else
31 # if HAVE_SYS_TIME_H
32 #  include <sys/time.h>
33 # else
34 #  include <time.h>
35 # endif
36 #endif
37
38 #include <sys/wait.h>
39 #include <string.h>
40 #include <limits.h>
41 #include <ctype.h>
42 #include <expat.h>
43 #include <libcitadel.h>
44 #include "citadel.h"
45 #include "server.h"
46 #include "citserver.h"
47 #include "support.h"
48 #include "config.h"
49 #include "user_ops.h"
50 #include "internet_addressing.h"
51 #include "md5.h"
52 #include "ctdl_module.h"
53 #include "serv_xmpp.h"
54
55
56 /*
57  * PLAIN authentication.  Returns zero on success, nonzero on failure.
58  */
59 int xmpp_auth_plain(char *authstring)
60 {
61         char decoded_authstring[1024];
62         char ident[256];
63         char user[256];
64         char pass[256];
65         int result;
66         long len;
67         int i;
68
69         /* Take apart the authentication string */
70         memset(pass, 0, sizeof(pass));
71
72         CtdlDecodeBase64(decoded_authstring, authstring, strlen(authstring));
73         safestrncpy(ident, decoded_authstring, sizeof ident);
74         safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
75         len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
76         if (len < 0)
77                 len = -len;
78
79         /* If there are underscores in either string, change them to spaces.  Some clients
80          * do not allow spaces so we can tell the user to substitute underscores if their
81          * login name contains spaces.
82          */
83         for (i=0; ident[i]!=0; ++i) {
84                 if (ident[i] == '_') {
85                         ident[i] = ' ';
86                 }
87         }
88         for (i=0; user[i]!=0; ++i) {
89                 if (user[i] == '_') {
90                         user[i] = ' ';
91                 }
92         }
93
94         /* Now attempt authentication */
95
96         if (!IsEmptyStr(ident)) {
97                 result = CtdlLoginExistingUser(ident);
98         }
99         else {
100                 result = CtdlLoginExistingUser(user);
101         }
102
103         if (result == login_ok) {
104                 if (CtdlTryPassword(pass, len) == pass_ok) {
105                         return(0);                              /* success */
106                 }
107         }
108
109         return(1);                                              /* failure */
110 }
111
112
113 /*
114  * Output the list of SASL mechanisms offered by this stream.
115  */
116 void xmpp_output_auth_mechs(void) {
117         cprintf("<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
118         cprintf("<mechanism>PLAIN</mechanism>");
119         cprintf("</mechanisms>");
120 }
121
122
123 /*
124  * Here we go ... client is trying to authenticate.
125  */
126 void xmpp_sasl_auth(char *sasl_auth_mech, char *authstring) {
127
128         if (strcasecmp(sasl_auth_mech, "PLAIN")) {
129                 cprintf("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
130                 cprintf("<invalid-mechanism/>");
131                 cprintf("</failure>");
132                 return;
133         }
134
135         if (CC->logged_in) {
136                 CtdlUserLogout();  /* Client may try to log in twice.  Handle this. */
137         }
138
139         if (CC->nologin) {
140                 cprintf("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
141                 cprintf("<system-shutdown/>");
142                 cprintf("</failure>");
143         }
144
145         else if (xmpp_auth_plain(authstring) == 0) {
146                 cprintf("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>");
147         }
148
149         else {
150                 cprintf("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
151                 cprintf("<not-authorized/>");
152                 cprintf("</failure>");
153         }
154 }
155
156
157 /*
158  * Non-SASL authentication
159  */
160 void xmpp_non_sasl_authenticate(char *iq_id, char *username, char *password) {
161         int result;
162         char xmlbuf[256];
163
164         if (CC->logged_in) {
165                 CtdlUserLogout();  /* Client may try to log in twice.  Handle this. */
166         }
167
168         result = CtdlLoginExistingUser(username);
169         if (result == login_ok) {
170                 result = CtdlTryPassword(password, strlen(password));
171                 if (result == pass_ok) {
172                         cprintf("<iq type=\"result\" id=\"%s\"></iq>", xmlesc(xmlbuf, iq_id, sizeof xmlbuf));   /* success */
173                         return;
174                 }
175         }
176
177         /* failure */
178         cprintf("<iq type=\"error\" id=\"%s\">", xmlesc(xmlbuf, iq_id, sizeof xmlbuf));
179         cprintf("<error code=\"401\" type=\"auth\">"
180                 "<not-authorized xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
181                 "</error>"
182                 "</iq>"
183         );
184 }