jabberd2  2.6.1
mod_verify.c
Go to the documentation of this file.
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 
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 
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 occured 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 
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 
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 adddress! "
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 
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 
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 
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 
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 
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;
289 
290  return 0;
291 }
user_t user
user this session belongs to
Definition: sm.h:256
pkt_type_t type
packet type
Definition: sm.h:138
jid_t jid
session jid (user@host/res)
Definition: sm.h:258
Definition: nad.h:93
static mod_ret_t _verify_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
Definition: mod_verify.c:188
data structures and prototypes for the session manager
static int _verify_user_load(mod_instance_t mi, user_t user)
Definition: mod_verify.c:248
#define NAD_CDATA_L(N, E)
Definition: nad.h:186
const char * id
component id
Definition: sm.h:168
const char * jid_user(jid_t jid)
expand and return the user
Definition: jid.c:338
static void check_code(verify_t *v, user_t user, pkt_t res, char *message)
Definition: mod_verify.c:137
static void print_instructions(pkt_t res)
Definition: mod_verify.c:174
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:346
single instance of a module in a chain
Definition: sm.h:446
log_t log
log context
Definition: sm.h:200
int init
number of times the module intialiser has been called
Definition: sm.h:416
void log_write(log_t log, int level, const char *msgfmt,...)
Definition: log.c:104
struct _verify_st verify_t
sm_t sm
sm context
Definition: sm.h:237
int nad_insert_elem(nad_t nad, unsigned int parent, int ns, const char *name, const char *cdata)
shove in a new child elem after the given one
Definition: nad.c:437
char * email
Definition: mod_verify.c:33
int index
module index.
Definition: sm.h:408
mm_t mm
module manager
Definition: sm.h:404
#define DLLEXPORT
Definition: c2s.h:47
static void send_email(verify_t *v, user_t user, pkt_t res, char *message)
Definition: mod_verify.c:40
void pool_cleanup(pool_t p, pool_cleanup_t f, void *arg)
public cleanup utils, insert in a way that they are run FIFO, before mem frees
Definition: pool.c:251
sm_t sm
sm context
Definition: sm.h:366
mod_ret_t(* in_sess)(mod_instance_t mi, sess_t sess, pkt_t pkt)
in-sess handler
Definition: sm.h:423
enum _verify_st::@0 state
pool_t p
memory pool this user is allocated off
Definition: sm.h:235
module_t mod
module that this is an instance of
Definition: sm.h:449
packet summary data wrapper
Definition: sm.h:129
storage_t st
storage subsystem
Definition: sm.h:211
nad_t nad
nad of the entire packet
Definition: sm.h:146
Definition: jid.h:42
void pkt_router(pkt_t pkt)
Definition: pkt.c:379
void pkt_free(pkt_t pkt)
Definition: pkt.c:315
#define log_debug(...)
Definition: log.h:65
char * code
Definition: mod_verify.c:34
packet was unhandled, should be passed to the next module
Definition: sm.h:340
static void _verify_user_delete(mod_instance_t mi, jid_t jid)
Definition: mod_verify.c:242
packet was handled (and freed)
Definition: sm.h:339
There is one instance of this struct per user who is logged in to this c2s instance.
Definition: c2s.h:74
int(* user_load)(mod_instance_t mi, user_t user)
user-load handler
Definition: sm.h:434
int nad_find_elem(nad_t nad, unsigned int elem, int ns, const char *name, int depth)
locate the next elem at a given depth with an optional matching name
Definition: nad.c:206
void(* user_delete)(mod_instance_t mi, jid_t jid)
user-delete handler
Definition: sm.h:438
message
Definition: sm.h:95
DLLEXPORT int module_init(mod_instance_t mi, char *arg)
Definition: mod_verify.c:280
#define NAD_CDATA(N, E)
Definition: nad.h:185
jid_t jid
user jid (user@host)
Definition: sm.h:239
#define ZONE
Definition: mio_impl.h:76
data for a single module
Definition: sm.h:403
void ** module_data
per-user module data
Definition: sm.h:249
static void _verify_user_free(verify_t *v)
Definition: mod_verify.c:232
pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from)
Definition: pkt.c:328
mod_ret_t
module return values
Definition: sm.h:338
sm_t sm
sm context
Definition: sm.h:447
#define NAD_ENS(N, E)
Definition: nad.h:196
data for a single user
Definition: sm.h:234