968cef950025830a79e5491153be821a8d0b7a2b
[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         char decoded_authstring[1024];
69         char ident[256];
70         char user[256];
71         char pass[256];
72         int result;
73         long len;
74
75
76         /* Take apart the authentication string */
77         memset(pass, 0, sizeof(pass));
78
79         CtdlDecodeBase64(decoded_authstring, authstring, strlen(authstring));
80         safestrncpy(ident, decoded_authstring, sizeof ident);
81         safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
82         len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
83         if (len < 0)
84                 len = -len;
85
86         /* If there are underscores in either string, change them to spaces.  Some clients
87          * do not allow spaces so we can tell the user to substitute underscores if their
88          * login name contains spaces.
89          */
90         convert_spaces_to_underscores(ident);
91         convert_spaces_to_underscores(user);
92
93         /* Now attempt authentication */
94
95         if (!IsEmptyStr(ident)) {
96                 result = CtdlLoginExistingUser(user, ident);
97         }
98         else {
99                 result = CtdlLoginExistingUser(NULL, user);
100         }
101
102         if (result == login_ok) {
103                 if (CtdlTryPassword(pass, len) == pass_ok) {
104                         return(0);                              /* success */
105                 }
106         }
107
108         return(1);                                              /* failure */
109 }
110
111
112 /*
113  * Output the list of SASL mechanisms offered by this stream.
114  */
115 void xmpp_output_auth_mechs(void) {
116         XPUT("<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
117              "<mechanism>PLAIN</mechanism>"
118              "</mechanisms>");
119 }
120
121 /*
122  * Here we go ... client is trying to authenticate.
123  */
124 void xmpp_sasl_auth(char *sasl_auth_mech, char *authstring) {
125
126         if (strcasecmp(sasl_auth_mech, "PLAIN")) {
127                 XPUT("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
128                      "<invalid-mechanism/>"
129                      "</failure>");
130                 return;
131         }
132
133         if (CC->logged_in) CtdlUserLogout();  /* Client may try to log in twice.  Handle this. */
134
135         if (CC->nologin) {
136                 XPUT("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
137                      "<system-shutdown/>"
138                      "</failure>");
139         }
140
141         else if (xmpp_auth_plain(authstring) == 0) {
142                 XPUT("<success xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>");
143         }
144
145         else {
146                 XPUT("<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">"
147                      "<not-authorized/>"
148                      "</failure>");
149         }
150 }
151
152
153
154 /*
155  * Non-SASL authentication
156  */
157 void xmpp_non_sasl_authenticate(char *iq_id, char *username, char *password, char *resource) {
158         int result;
159
160         if (CC->logged_in) CtdlUserLogout();  /* Client may try to log in twice.  Handle this. */
161
162         result = CtdlLoginExistingUser(NULL, username);
163         if (result == login_ok) {
164                 result = CtdlTryPassword(password, strlen(password));
165                 if (result == pass_ok) {
166                         XPUT("<iq type=\"result\" id=\"");
167                         XPutProp(iq_id, strlen(iq_id));
168                         XPUT("\"></iq>"); /* success */
169                         return;
170                 }
171         }
172
173         /* failure */
174         XPUT("<iq type=\"error\" id=\"");
175         XPutProp(iq_id, strlen(iq_id));
176         XPUT("\">"
177              "<error code=\"401\" type=\"auth\">"
178              "<not-authorized xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>"
179              "</error>"
180              "</iq>"
181         );
182 }