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