jabberd2  2.3.6
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  goto error;
76  }
77  if (pclose(pipe) == -1) {
78  log_write(user->sm->log, LOG_ERR, "Error closing email code for %s from 'pwgen'. %d:%s", v->email, errno, strerror(errno));
79  goto error;
80  }
81  log_debug(ZONE, "code: >%s<", v->code);
82  if ((pipe = popen("sendmail -t -F 'Jabber Server'", "w")) == NULL) {
83  log_write(user->sm->log, LOG_ERR, "Error starting sendmail to %s. %d:%s", v->email, errno, strerror(errno));
84  goto error;
85  }
86 
87  os = os_new();
88  o = os_object_new(os);
89  os_object_put(o, "email", v->email, os_type_STRING);
90  os_object_put(o, "code", v->code, os_type_STRING);
91  os_object_put(o, "state", &v->state, os_type_INTEGER);
92  if (storage_replace(user->sm->st, "verify", jid_user(user->jid), NULL, os) != st_SUCCESS) {
93  log_write(user->sm->log, LOG_ERR, "Error writing email code to DB for %s", v->email);
94  free(v->email);
95  free(v->code);
96  v->email=NULL;
97  v->code=NULL;
98  }
99  os_free(os);
100 
101  if (fprintf(pipe,
102  "To: %s\n"
103  "Subject: Jabberd email verification\n"
104  "\n"
105  "Please reply the following line to the jabber server to confirm your email address.\n\n"
106  "code: %s\n"
107  ".\n", v->email, v->code) < 0) {
108  log_write(user->sm->log, LOG_ERR, "Error writing sendmail to %s. %d:%s", v->email, errno, strerror(errno));
109  goto error;
110  }
111  if (pclose(pipe) == -1) {
112  log_write(user->sm->log, LOG_ERR, "Error closing sendmail to %s. %d:%s", v->email, errno, strerror(errno));
113  goto error;
114  }
115  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
116  "subject", "Verification email sent");
117  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
118  "A verification email has been sent to the specified "
119  "address. Please check your inbox and follow the "
120  "instructions given in the mail.");
121  goto free;
122 
123 error:
124  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
125  "subject", "Error");
126  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
127  "An error occured while trying to send the verification email to you.\n"
128  "Please try again later. If the problem persists, please contact the\n"
129  "server admin.");
130 free:
131  free(message);
132  return;
133 }
134 
135 static void check_code(verify_t *v, user_t user, pkt_t res, char *message)
136 {
137  os_t os;
138  os_object_t o;
139 
140  if (v->code == NULL) {
141  print_instructions(res);
142  return;
143  }
144  if (strstr(message, v->code) != NULL) {
145  v->state = VERIFIED;
146  log_debug(ZONE, "check_code: VERIFIED");
147 
148  os = os_new();
149  o = os_object_new(os);
150  os_object_put(o, "email", v->email, os_type_STRING);
151  os_object_put(o, "code", v->code, os_type_STRING);
152  os_object_put(o, "state", &v->state, os_type_INTEGER);
153  if (storage_replace(user->sm->st, "verify", jid_user(user->jid), NULL, os) != st_SUCCESS) {
154  log_write(user->sm->log, LOG_ERR, "Error writing verification state to DB for %s", v->email);
155  }
156  os_free(os);
157  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
158  "subject", "Code accepted");
159  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
160  "Your verification code has been accepted.\n"
161  "You are now a verified user.");
162  } else {
163  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
164  "subject", "Code rejected");
165  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
166  "Your verification code did not match.\n"
167  "Please try to re-submit it, or send another \n"
168  "\"email: \" line to gat a new code sent to you.");
169  }
170 }
171 
172 static void print_instructions(pkt_t res)
173 {
174  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1),
175  "subject", "Please enter your email address");
176  nad_insert_elem(res->nad, 1, NAD_ENS(res->nad, 1), "body",
177  "You are blocked from this jabber server until "
178  "you have entered and validated your email adddress! "
179  "To do this, please type in \"email: \" followed by "
180  "your email address as a reply to this message, e.g.\n\n"
181  "email: johndoe@example.com\n\n"
182  "A verification code with further instructions will then "
183  "be sent to that email address.");
184 }
185 
187 {
188  pkt_t res;
189  nad_t nad = pkt->nad;
190  int body, message;
191  char *cdata= NULL;
192  verify_t *v = sess->user->module_data[mi->mod->index];
193 
194  log_debug(ZONE, "_verify_in_sess: %d", v->state);
195 
196  if(v->state == VERIFIED || !(pkt->type & pkt_MESSAGE))
197  return mod_PASS;
198 
199  log_debug(ZONE, "blocking message from from %s", jid_full(sess->jid));
200 
201  message = nad_find_elem(nad, 0, -1, "message", 1);
202  log_debug(ZONE, "message: %d", message);
203  body = nad_find_elem(nad, message, -1, "body", 1);
204  log_debug(ZONE, "body: %d", body);
205  if (body >= 0) {
206  size_t len = NAD_CDATA_L(nad, body);
207  cdata = strncpy(malloc(len+1), NAD_CDATA(nad, body), len);
208  cdata[len] = '\0';
209  log_debug(ZONE, "---> %s <---", cdata);
210  res = pkt_create(mi->mod->mm->sm, "message", NULL, jid_full(sess->jid),
211  mi->mod->mm->sm->id);
212  if (strstr(cdata, "email: ") == cdata) {
213  send_email(v, sess->user, res, cdata);
214  } else if (strstr(cdata, "code: ") == cdata) {
215  check_code(v, sess->user, res, cdata);
216  } else {
217  print_instructions(res);
218  }
219  pkt_router(res);
220  }
221  pkt_free(pkt);
222 
223  return mod_HANDLED;
224 }
225 
227 {
228  log_debug(ZONE, "_verify_user_free");
229  if (v->email != NULL)
230  free(v->email);
231  if (v->code != NULL)
232  free(v->code);
233  free(v);
234 }
235 
237 {
238  log_debug(ZONE, "deleting email verification for %s", jid_user(jid));
239  storage_delete(mi->sm->st, "verify", jid_user(jid), NULL);
240 }
241 
243 {
244  verify_t *v;
245  os_t os;
246  os_object_t o;
247  int state;
248 
249  log_debug(ZONE, "_verify_user_load: >%s<", jid_user(user->jid));
250  v = calloc(1, sizeof(struct _verify_st));
251  user->module_data[mi->mod->index] = v;
252  if (storage_get(user->sm->st, "verify", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
253  if (os_iter_first(os)) {
254  o = os_iter_object(os);
255  if (os_object_get_str(os, o, "email", &v->email) &&
256  os_object_get_str(os, o, "code", &v->code) &&
257  os_object_get_int(os, o, "state", &state)) {
258  v->email = strdup(v->email);
259  v->code = strdup(v->code);
260  v->state = ( state == VERIFIED ) ? VERIFIED : UNVERIFIED;
261  } else {
262  v->state = UNVERIFIED;
263  v->email = NULL;
264  v->code = NULL;
265  }
266  }
267  os_free(os);
268  }
269  log_debug(ZONE, "_verify_user_load: state=%d<", v->state);
270  pool_cleanup(user->p, (void (*))(void *) _verify_user_free, v);
271  return 0;
272 }
273 
275  module_t mod = mi->mod;
276 
277  if(mod->init) return 0;
278 
279  log_debug(ZONE, "mod_verify:init: %p", mi);
280  mod->in_sess = _verify_in_sess;
283 
284  return 0;
285 }
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:186
data structures and prototypes for the session manager
int nad_insert_elem(nad_t nad, int parent, int ns, const char *name, const char *cdata)
shove in a new child elem after the given one
Definition: nad.c:433
static int _verify_user_load(mod_instance_t mi, user_t user)
Definition: mod_verify.c:242
#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:339
static void check_code(verify_t *v, user_t user, pkt_t res, char *message)
Definition: mod_verify.c:135
static void print_instructions(pkt_t res)
Definition: mod_verify.c:172
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:347
single instance of a module in a chain
Definition: sm.h:446
log_t log
log context
Definition: sm.h:200
int nad_find_elem(nad_t nad, 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:204
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
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:236
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
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:274
#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:226
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