1 /*
2 * jabberd - Jabber Open Source Server
3 * Copyright (c) 2009 Reinhard Max
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
18 */
19
20 #include <sys/types.h>
21 #include <regex.h>
22 #include <string.h>
23
24 #include "sm.h"
25
26 /** @file sm/mod_verify.c
27 * @brief verify users using e-mail
28 * @author Reinhard Max
29 */
30
31 typedef struct _verify_st {
32 enum {UNVERIFIED = 0, VERIFIED} state;
33 char *email;
34 char *code;
35 } verify_t;
36
37 static void print_instructions(pkt_t res);
38
39
send_email(verify_t * v,user_t user,pkt_t res,char * message)40 static void send_email(verify_t *v, user_t user, pkt_t res, char *message)
41 {
42 FILE *pipe;
43 regex_t preg;
44 regmatch_t match[1];
45 int result;
46 os_t os;
47 os_object_t o;
48
49 message = strdup(message);
50 result = regcomp(&preg, "[a-z0-9._+-]+@[a-z0-9.-]+", REG_EXTENDED|REG_ICASE);
51 result |= regexec(&preg, message, 1, match, 0);
52 regfree(&preg);
53
54 if (result != 0 || match[0].rm_so == -1) {
55 print_instructions(res);
56 goto free;
57 }
58
59 v->state = UNVERIFIED;
60 if (v->email != NULL)
61 free(v->email);
62 *(message + match[0].rm_eo) = '\0';
63 v->email = strdup(message + match[0].rm_so);
64 log_debug(ZONE, "email: >%s<", v->email);
65
66 if (v->code != NULL)
67 free(v->code);
68 v->code = calloc(1,11);
69 if ((pipe = popen("pwgen 10 1", "r")) == NULL) {
70 log_write(user->sm->log, LOG_ERR, "Error generating email code for %s using 'pwgen'. %d:%s", v->email, errno, strerror(errno));
71 goto error;
72 }
73 if (fgets(v->code, 11, pipe) == NULL) {
74 log_write(user->sm->log, LOG_ERR, "Error getting email code for %s from 'pwgen'. %d:%s", v->email, errno, strerror(errno));
75 pclose(pipe);
76 goto error;
77 }
78 if (pclose(pipe) == -1) {
79 log_write(user->sm->log, LOG_ERR, "Error closing email code for %s from 'pwgen'. %d:%s", v->email, errno, strerror(errno));
80 goto error;
81 }
82 log_debug(ZONE, "code: >%s<", v->code);
83 if ((pipe = popen("sendmail -t -F 'Jabber Server'", "w")) == NULL) {
84 log_write(user->sm->log, LOG_ERR, "Error starting sendmail to %s. %d:%s", v->email, errno, strerror(errno));
85 goto error;
86 }
87
88 os = os_new();
89 o = os_object_new(os);
90 os_object_put(o, "email", v->email, os_type_STRING);
91 os_object_put(o, "code", v->code, os_type_STRING);
92 os_object_put(o, "state", &v->state, os_type_INTEGER);
93 if (storage_replace(user->sm->st, "verify", jid_user(user->jid), NULL, os) != st_SUCCESS) {
94 log_write(user->sm->log, LOG_ERR, "Error writing email code to DB for %s", v->email);
95 free(v->email);
96 free(v->code);
97 v->email=NULL;
98 v->code=NULL;
99 }
100 os_free(os);
101
102 if (fprintf(pipe,
103 "To: %s\n"
104 "Subject: Jabberd email verification\n"
105 "\n"
106 "Please reply the following line to the jabber server to confirm your email address.\n\n"
107 "code: %s\n"
108 ".\n", v->email, v->code) < 0) {
109 log_write(user->sm->log, LOG_ERR, "Error writing sendmail to %s. %d:%s", v->email, errno, strerror(errno));
110 pclose(pipe);
111 goto error;
112 }
113 if (pclose(pipe) == -1) {
114 log_write(user->sm->log, LOG_ERR, "Error closing sendmail to %s. %d:%s", v->email, errno, strerror(errno));
115 goto error;
116 }
117 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
118 "subject", "Verification email sent");
119 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
120 "A verification email has been sent to the specified "
121 "address. Please check your inbox and follow the "
122 "instructions given in the mail.");
123 goto free;
124
125 error:
126 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
127 "subject", "Error");
128 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
129 "An error occurred while trying to send the verification email to you.\n"
130 "Please try again later. If the problem persists, please contact the\n"
131 "server admin.");
132 free:
133 free(message);
134 return;
135 }
136
check_code(verify_t * v,user_t user,pkt_t res,char * message)137 static void check_code(verify_t *v, user_t user, pkt_t res, char *message)
138 {
139 os_t os;
140 os_object_t o;
141
142 if (v->code == NULL) {
143 print_instructions(res);
144 return;
145 }
146 if (strstr(message, v->code) != NULL) {
147 v->state = VERIFIED;
148 log_debug(ZONE, "check_code: VERIFIED");
149
150 os = os_new();
151 o = os_object_new(os);
152 os_object_put(o, "email", v->email, os_type_STRING);
153 os_object_put(o, "code", v->code, os_type_STRING);
154 os_object_put(o, "state", &v->state, os_type_INTEGER);
155 if (storage_replace(user->sm->st, "verify", jid_user(user->jid), NULL, os) != st_SUCCESS) {
156 log_write(user->sm->log, LOG_ERR, "Error writing verification state to DB for %s", v->email);
157 }
158 os_free(os);
159 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
160 "subject", "Code accepted");
161 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
162 "Your verification code has been accepted.\n"
163 "You are now a verified user.");
164 } else {
165 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
166 "subject", "Code rejected");
167 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
168 "Your verification code did not match.\n"
169 "Please try to re-submit it, or send another \n"
170 "\"email: \" line to gat a new code sent to you.");
171 }
172 }
173
print_instructions(pkt_t res)174 static void print_instructions(pkt_t res)
175 {
176 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
177 "subject", "Please enter your email address");
178 nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
179 "You are blocked from this jabber server until "
180 "you have entered and validated your email address! "
181 "To do this, please type in \"email: \" followed by "
182 "your email address as a reply to this message, e.g.\n\n"
183 "email: johndoe@example.com\n\n"
184 "A verification code with further instructions will then "
185 "be sent to that email address.");
186 }
187
_verify_in_sess(mod_instance_t mi,sess_t sess,pkt_t pkt)188 static mod_ret_t _verify_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
189 {
190 pkt_t res;
191 nad_t nad = pkt->nad;
192 int body, message;
193 char *cdata= NULL;
194 verify_t *v = sess->user->module_data[mi->mod->index];
195
196 log_debug(ZONE, "_verify_in_sess: %d", v->state);
197
198 if(v->state == VERIFIED || !(pkt->type & pkt_MESSAGE))
199 return mod_PASS;
200
201 log_debug(ZONE, "blocking message from from %s", jid_full(sess->jid));
202
203 message = nad_find_elem(nad, 0, -1, "message", 1);
204 log_debug(ZONE, "message: %d", message);
205 if (message >= 0) {
206 body = nad_find_elem(nad, message, -1, "body", 1);
207 log_debug(ZONE, "body: %d", body);
208 if (body >= 0) {
209 size_t len = NAD_CDATA_L(nad, body);
210 cdata = malloc(len+1);
211 strncpy(cdata, NAD_CDATA(nad, body), len);
212 cdata[len] = '\0';
213 log_debug(ZONE, "---> %s <---", cdata);
214 res = pkt_create(mi->mod->mm->sm, "message", NULL, jid_full(sess->jid),
215 mi->mod->mm->sm->id);
216 if (strstr(cdata, "email: ") == cdata) {
217 send_email(v, sess->user, res, cdata);
218 } else if (strstr(cdata, "code: ") == cdata) {
219 check_code(v, sess->user, res, cdata);
220 } else {
221 print_instructions(res);
222 }
223 pkt_router(res);
224 free(cdata);
225 }
226 }
227
228 pkt_free(pkt);
229 return mod_HANDLED;
230 }
231
_verify_user_free(verify_t * v)232 static void _verify_user_free(verify_t *v)
233 {
234 log_debug(ZONE, "_verify_user_free");
235 if (v->email != NULL)
236 free(v->email);
237 if (v->code != NULL)
238 free(v->code);
239 free(v);
240 }
241
_verify_user_delete(mod_instance_t mi,jid_t jid)242 static void _verify_user_delete(mod_instance_t mi, jid_t jid)
243 {
244 log_debug(ZONE, "deleting email verification for %s", jid_user(jid));
245 storage_delete(mi->sm->st, "verify", jid_user(jid), NULL);
246 }
247
_verify_user_load(mod_instance_t mi,user_t user)248 static int _verify_user_load(mod_instance_t mi, user_t user)
249 {
250 verify_t *v;
251 os_t os;
252 os_object_t o;
253 int state;
254
255 log_debug(ZONE, "_verify_user_load: >%s<", jid_user(user->jid));
256 v = calloc(1, sizeof(struct _verify_st));
257 user->module_data[mi->mod->index] = v;
258 if (storage_get(user->sm->st, "verify", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
259 if (os_iter_first(os)) {
260 o = os_iter_object(os);
261 if (os_object_get_str(os, o, "email", &v->email) &&
262 os_object_get_str(os, o, "code", &v->code) &&
263 os_object_get_int(os, o, "state", &state)) {
264 v->email = strdup(v->email);
265 v->code = strdup(v->code);
266 v->state = ( state == VERIFIED ) ? VERIFIED : UNVERIFIED;
267 } else {
268 v->state = UNVERIFIED;
269 v->email = NULL;
270 v->code = NULL;
271 }
272 }
273 os_free(os);
274 }
275 log_debug(ZONE, "_verify_user_load: state=%d<", v->state);
276 pool_cleanup(user->p, (void (*))(void *) _verify_user_free, v);
277 return 0;
278 }
279
module_init(mod_instance_t mi,char * arg)280 DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
281 module_t mod = mi->mod;
282
283 if(mod->init) return 0;
284
285 log_debug(ZONE, "mod_verify:init: %p", mi);
286 mod->in_sess = _verify_in_sess;
287 mod->user_load = _verify_user_load;
288 mod->user_delete = _verify_user_delete;
289
290 return 0;
291 }
292