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