jabberd2  2.2.17
authreg.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4  * Ryan Eatmon, Robert Norris
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19  */
20 
21 #include "c2s.h"
22 #include <stringprep.h>
23 #ifdef _WIN32
24  #include <windows.h>
25  #define LIBRARY_DIR "."
26 #else
27  #include <dlfcn.h>
28 #endif
29 
30 /* authreg module manager */
31 
32 typedef struct _authreg_error_st {
33  char *class;
34  char *name;
35  char *code;
36  char *uri;
38 
40 authreg_t authreg_init(c2s_t c2s, char *name) {
41  char mod_fullpath[PATH_MAX], *modules_path;
42  ar_module_init_fn init_fn = NULL;
43  authreg_t ar;
44  void *handle;
45 
46  /* load authreg module */
47  modules_path = config_get_one(c2s->config, "authreg.path", 0);
48  if (modules_path != NULL)
49  log_write(c2s->log, LOG_NOTICE, "modules search path: %s", modules_path);
50  else
51  log_write(c2s->log, LOG_NOTICE, "modules search path undefined, using default: "LIBRARY_DIR);
52 
53  log_write(c2s->log, LOG_INFO, "loading '%s' authreg module", name);
54 #ifndef _WIN32
55  if (modules_path != NULL)
56  snprintf(mod_fullpath, PATH_MAX, "%s/authreg_%s.so", modules_path, name);
57  else
58  snprintf(mod_fullpath, PATH_MAX, "%s/authreg_%s.so", LIBRARY_DIR, name);
59  handle = dlopen(mod_fullpath, RTLD_LAZY);
60  if (handle != NULL)
61  init_fn = dlsym(handle, "ar_init");
62 #else
63  if (modules_path != NULL)
64  snprintf(mod_fullpath, PATH_MAX, "%s\\authreg_%s.dll", modules_path, name);
65  else
66  snprintf(mod_fullpath, PATH_MAX, "authreg_%s.dll", name);
67  handle = (void*) LoadLibrary(mod_fullpath);
68  if (handle != NULL)
69  init_fn = (ar_module_init_fn)GetProcAddress((HMODULE) handle, "ar_init");
70 #endif
71 
72  if (handle != NULL && init_fn != NULL) {
73  log_debug(ZONE, "preloaded module '%s' (not initialized yet)", name);
74  } else {
75 #ifndef _WIN32
76  log_write(c2s->log, LOG_ERR, "failed loading authreg module '%s' (%s)", name, dlerror());
77  if (handle != NULL)
78  dlclose(handle);
79 #else
80  log_write(c2s->log, LOG_ERR, "failed loading authreg module '%s' (errcode: %x)", name, GetLastError());
81  if (handle != NULL)
82  FreeLibrary((HMODULE) handle);
83 #endif
84  return NULL;
85  }
86 
87  /* make a new one */
88  ar = (authreg_t) calloc(1, sizeof(struct authreg_st));
89 
90  ar->c2s = c2s;
91 
92  /* call the initialiser */
93  if((init_fn)(ar) != 0)
94  {
95  log_write(c2s->log, LOG_ERR, "failed to initialize auth module '%s'", name);
96  authreg_free(ar);
97  return NULL;
98  }
99 
100  /* we need user_exists(), at the very least */
101  if(ar->user_exists == NULL)
102  {
103  log_write(c2s->log, LOG_ERR, "auth module '%s' has no check for user existence", name);
104  authreg_free(ar);
105  return NULL;
106  }
107 
108  /* its good */
109  log_write(c2s->log, LOG_NOTICE, "initialized auth module '%s'", name);
110 
111  return ar;
112 }
113 
116  if (ar) {
117  if(ar->free != NULL) (ar->free)(ar);
118  free(ar);
119  }
120 }
121 
123 inline static void _authreg_auth_log(c2s_t c2s, sess_t sess, char *method, char *username, char *resource, int success) {
124  log_write(c2s->log, LOG_NOTICE, "[%d] %s authentication %s: %s@%s/%s %s:%d%s%s",
125  sess->s->tag, method, success ? "succeeded" : "failed",
126  username, sess->host->realm, resource,
127  sess->s->ip, sess->s->port,
128  sess->s->ssf ? " TLS" : "", sess->s->compressed ? " ZLIB" : ""
129  );
130 }
131 
133 static void _authreg_auth_get(c2s_t c2s, sess_t sess, nad_t nad) {
134  int ns, elem, attr;
135  char username[1024], id[128];
136  int ar_mechs;
137 
138  /* can't auth if they're active */
139  if(sess->active) {
141  return;
142  }
143 
144  /* sort out the username */
145  ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL);
146  elem = nad_find_elem(nad, 1, ns, "username", 1);
147  if(elem < 0)
148  {
149  log_debug(ZONE, "auth get with no username, bouncing it");
150 
152 
153  return;
154  }
155 
156  snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
157  if(stringprep_xmpp_nodeprep(username, 1024) != 0) {
158  log_debug(ZONE, "auth get username failed nodeprep, bouncing it");
160  return;
161  }
162 
163  ar_mechs = c2s->ar_mechanisms;
164  if (sess->s->ssf>0)
165  ar_mechs = ar_mechs | c2s->ar_ssl_mechanisms;
166 
167  /* no point going on if we have no mechanisms */
168  if(!(ar_mechs & (AR_MECH_TRAD_PLAIN | AR_MECH_TRAD_DIGEST))) {
170  return;
171  }
172 
173  /* do we have the user? */
174  if((c2s->ar->user_exists)(c2s->ar, username, sess->host->realm) == 0) {
176  return;
177  }
178 
179  /* extract the id */
180  attr = nad_find_attr(nad, 0, -1, "id", NULL);
181  if(attr >= 0)
182  snprintf(id, 128, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
183 
184  nad_free(nad);
185 
186  /* build a result packet */
187  nad = nad_new();
188 
189  ns = nad_add_namespace(nad, uri_CLIENT, NULL);
190 
191  nad_append_elem(nad, ns, "iq", 0);
192  nad_append_attr(nad, -1, "type", "result");
193 
194  if(attr >= 0)
195  nad_append_attr(nad, -1, "id", id);
196 
197  ns = nad_add_namespace(nad, uri_AUTH, NULL);
198  nad_append_elem(nad, ns, "query", 1);
199 
200  nad_append_elem(nad, ns, "username", 2);
201  nad_append_cdata(nad, username, strlen(username), 3);
202 
203  nad_append_elem(nad, ns, "resource", 2);
204 
205  /* fill out the packet with available auth mechanisms */
206  if(ar_mechs & AR_MECH_TRAD_PLAIN && (c2s->ar->get_password != NULL || c2s->ar->check_password != NULL))
207  nad_append_elem(nad, ns, "password", 2);
208 
209  if(ar_mechs & AR_MECH_TRAD_DIGEST && c2s->ar->get_password != NULL)
210  nad_append_elem(nad, ns, "digest", 2);
211 
212  /* give it back to the client */
213  sx_nad_write(sess->s, nad);
214 
215  return;
216 }
217 
219 static void _authreg_auth_set(c2s_t c2s, sess_t sess, nad_t nad) {
220  int ns, elem, attr, authd = 0;
221  char username[1024], resource[1024], str[1024], hash[280];
222  int ar_mechs;
223 
224  /* can't auth if they're active */
225  if(sess->active) {
227  return;
228  }
229 
230  ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL);
231 
232  /* sort out the username */
233  elem = nad_find_elem(nad, 1, ns, "username", 1);
234  if(elem < 0)
235  {
236  log_debug(ZONE, "auth set with no username, bouncing it");
237 
239 
240  return;
241  }
242 
243  snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
244  if(stringprep_xmpp_nodeprep(username, 1024) != 0) {
245  log_debug(ZONE, "auth set username failed nodeprep, bouncing it");
247  return;
248  }
249 
250  /* make sure we have the resource */
251  elem = nad_find_elem(nad, 1, ns, "resource", 1);
252  if(elem < 0)
253  {
254  log_debug(ZONE, "auth set with no resource, bouncing it");
255 
257 
258  return;
259  }
260 
261  snprintf(resource, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
262  if(stringprep_xmpp_resourceprep(resource, 1024) != 0) {
263  log_debug(ZONE, "auth set resource failed resourceprep, bouncing it");
265  return;
266  }
267 
268  ar_mechs = c2s->ar_mechanisms;
269  if (sess->s->ssf > 0)
270  ar_mechs = ar_mechs | c2s->ar_ssl_mechanisms;
271 
272  /* no point going on if we have no mechanisms */
273  if(!(ar_mechs & (AR_MECH_TRAD_PLAIN | AR_MECH_TRAD_DIGEST))) {
275  return;
276  }
277 
278  /* do we have the user? */
279  if((c2s->ar->user_exists)(c2s->ar, username, sess->host->realm) == 0) {
281  return;
282  }
283 
284  /* digest auth */
285  if(!authd && ar_mechs & AR_MECH_TRAD_DIGEST && c2s->ar->get_password != NULL)
286  {
287  elem = nad_find_elem(nad, 1, ns, "digest", 1);
288  if(elem >= 0)
289  {
290  if((c2s->ar->get_password)(c2s->ar, username, sess->host->realm, str) == 0)
291  {
292  snprintf(hash, 280, "%s%s", sess->s->id, str);
293  shahash_r(hash, hash);
294 
295  if(strlen(hash) == NAD_CDATA_L(nad, elem) && strncmp(hash, NAD_CDATA(nad, elem), NAD_CDATA_L(nad, elem)) == 0)
296  {
297  log_debug(ZONE, "digest auth succeeded");
298  authd = 1;
299  _authreg_auth_log(c2s, sess, "traditional.digest", username, resource, TRUE);
300  }
301  }
302  }
303  }
304 
305  /* plaintext auth (compare) */
306  if(!authd && ar_mechs & AR_MECH_TRAD_PLAIN && c2s->ar->get_password != NULL)
307  {
308  elem = nad_find_elem(nad, 1, ns, "password", 1);
309  if(elem >= 0)
310  {
311  if((c2s->ar->get_password)(c2s->ar, username, sess->host->realm, str) == 0 && strlen(str) == NAD_CDATA_L(nad, elem) && strncmp(str, NAD_CDATA(nad, elem), NAD_CDATA_L(nad, elem)) == 0)
312  {
313  log_debug(ZONE, "plaintext auth (compare) succeeded");
314  authd = 1;
315  _authreg_auth_log(c2s, sess, "traditional.plain(compare)", username, resource, TRUE);
316  }
317  }
318  }
319 
320  /* plaintext auth (check) */
321  if(!authd && ar_mechs & AR_MECH_TRAD_PLAIN && c2s->ar->check_password != NULL)
322  {
323  elem = nad_find_elem(nad, 1, ns, "password", 1);
324  if(elem >= 0)
325  {
326  snprintf(str, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
327  if((c2s->ar->check_password)(c2s->ar, username, sess->host->realm, str) == 0)
328  {
329  log_debug(ZONE, "plaintext auth (check) succeded");
330  authd = 1;
331  _authreg_auth_log(c2s, sess, "traditional.plain", username, resource, TRUE);
332  }
333  }
334  }
335 
336  /* now, are they authenticated? */
337  if(authd)
338  {
339  /* create new bound jid holder */
340  if(sess->resources == NULL) {
341  sess->resources = (bres_t) calloc(1, sizeof(struct bres_st));
342  }
343 
344  /* our local id */
345  sprintf(sess->resources->c2s_id, "%d", sess->s->tag);
346 
347  /* the full user jid for this session */
348  sess->resources->jid = jid_new(sess->s->req_to, -1);
349  jid_reset_components(sess->resources->jid, username, sess->resources->jid->domain, resource);
350 
351  log_write(sess->c2s->log, LOG_NOTICE, "[%d] requesting session: jid=%s", sess->s->tag, jid_full(sess->resources->jid));
352 
353  /* build a result packet, we'll send this back to the client after we have a session for them */
354  sess->result = nad_new();
355 
356  ns = nad_add_namespace(sess->result, uri_CLIENT, NULL);
357 
358  nad_append_elem(sess->result, ns, "iq", 0);
359  nad_set_attr(sess->result, 0, -1, "type", "result", 6);
360 
361  attr = nad_find_attr(nad, 0, -1, "id", NULL);
362  if(attr >= 0)
363  nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
364 
365  /* start a session with the sm */
366  sm_start(sess, sess->resources);
367 
368  /* finished with the nad */
369  nad_free(nad);
370 
371  return;
372  }
373 
374  _authreg_auth_log(c2s, sess, "traditional", username, resource, FALSE);
375 
376  /* auth failed, so error */
378 
379  return;
380 }
381 
383 static void _authreg_register_get(c2s_t c2s, sess_t sess, nad_t nad) {
384  int attr, ns;
385  char id[128];
386 
387  /* registrations can happen if reg is enabled and we can create users and set passwords */
388  if(sess->active || !(c2s->ar->set_password != NULL && c2s->ar->create_user != NULL && (sess->host->ar_register_enable || sess->host->ar_register_oob))) {
390  return;
391  }
392 
393  /* extract the id */
394  attr = nad_find_attr(nad, 0, -1, "id", NULL);
395  if(attr >= 0)
396  snprintf(id, 128, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));
397 
398  nad_free(nad);
399 
400  /* build a result packet */
401  nad = nad_new();
402 
403  ns = nad_add_namespace(nad, uri_CLIENT, NULL);
404 
405  nad_append_elem(nad, ns, "iq", 0);
406  nad_append_attr(nad, -1, "type", "result");
407 
408  if(attr >= 0)
409  nad_append_attr(nad, -1, "id", id);
410 
411  ns = nad_add_namespace(nad, uri_REGISTER, NULL);
412  nad_append_elem(nad, ns, "query", 1);
413 
414  nad_append_elem(nad, ns, "instructions", 2);
416 
417  if(sess->host->ar_register_enable) {
418  nad_append_elem(nad, ns, "username", 2);
419  nad_append_elem(nad, ns, "password", 2);
420  }
421 
422  if(sess->host->ar_register_oob) {
423  int ns = nad_add_namespace(nad, uri_OOB, NULL);
424  nad_append_elem(nad, ns, "x", 2);
425  nad_append_elem(nad, ns, "url", 3);
426  nad_append_cdata(nad, sess->host->ar_register_oob, strlen(sess->host->ar_register_oob), 4);
427  }
428 
429  /* give it back to the client */
430  sx_nad_write(sess->s, nad);
431 }
432 
434 static void _authreg_register_set(c2s_t c2s, sess_t sess, nad_t nad)
435 {
436  int ns = 0, elem, attr;
437  char username[1024], password[1024];
438 
439  /* if we're not configured for registration (or pw changes), or we can't set passwords, fail outright */
440  if(!(sess->host->ar_register_enable || sess->host->ar_register_password) || c2s->ar->set_password == NULL) {
442  return;
443  }
444 
445  ns = nad_find_scoped_namespace(nad, uri_REGISTER, NULL);
446 
447  /* removals */
448  if(sess->active && nad_find_elem(nad, 1, ns, "remove", 1) >= 0) {
449  /* only if full reg is enabled */
450  if(!sess->host->ar_register_enable) {
452  return;
453  }
454 
455  log_debug(ZONE, "user remove requested");
456 
457  /* make sure we can delete them */
458  if(c2s->ar->delete_user == NULL) {
460  return;
461  }
462 
463  /* otherwise, delete them */
464  if((c2s->ar->delete_user)(c2s->ar, sess->resources->jid->node, sess->host->realm) != 0) {
465  log_debug(ZONE, "user delete failed");
467  return;
468  }
469 
470  log_write(c2s->log, LOG_NOTICE, "[%d] deleted user: user=%s; realm=%s", sess->s->tag, sess->resources->jid->node, sess->host->realm);
471 
472  log_write(c2s->log, LOG_NOTICE, "[%d] registration remove succeeded, requesting user deletion: jid=%s", sess->s->tag, jid_user(sess->resources->jid));
473 
474  /* make a result nad */
475  sess->result = nad_new();
476 
477  ns = nad_add_namespace(sess->result, uri_CLIENT, NULL);
478 
479  nad_append_elem(sess->result, ns, "iq", 0);
480  nad_set_attr(sess->result, 0, -1, "type", "result", 6);
481 
482  /* extract the id */
483  attr = nad_find_attr(nad, 0, -1, "id", NULL);
484  if(attr >= 0)
485  nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
486 
487  nad_free(nad);
488 
489  sx_nad_write(sess->s, sess->result);
490  sess->result = NULL;
491 
492  /* get the sm to delete them (it will force their sessions to end) */
493  sm_delete(sess, sess->resources);
494 
495  return;
496  }
497 
498  /* username is required */
499  elem = nad_find_elem(nad, 1, ns, "username", 1);
500  if(elem < 0)
501  {
502  log_debug(ZONE, "register set with no username, bouncing it");
504  return;
505  }
506 
507  snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
508  if(stringprep_xmpp_nodeprep(username, 1024) != 0) {
509  log_debug(ZONE, "register set username failed nodeprep, bouncing it");
511  return;
512  }
513 
514  elem = nad_find_elem(nad, 1, ns, "password", 1);
515  if(elem < 0)
516  {
517  log_debug(ZONE, "register set with no password, bouncing it");
519  return;
520  }
521 
522  /* if they're already auth'd, its a password change */
523  if(sess->active)
524  {
525  /* confirm that the username matches their auth id */
526  if(strcmp(username, sess->resources->jid->node) != 0)
527  {
528  log_debug(ZONE, "%s is trying to change password for %s, bouncing it", jid_full(sess->resources->jid), username);
530  return;
531  }
532  }
533 
534  /* can't go on if we're not doing full reg */
535  else if(!sess->host->ar_register_enable) {
537  return;
538  }
539 
540  /* if they exist, bounce */
541  else if((c2s->ar->user_exists)(c2s->ar, username, sess->host->realm))
542  {
543  log_debug(ZONE, "attempt to register %s, but they already exist", username);
545  return;
546  }
547 
548  /* make sure we can create them */
549  else if(c2s->ar->create_user == NULL)
550  {
552  return;
553  }
554 
555  /* otherwise, create them */
556  else if((c2s->ar->create_user)(c2s->ar, username, sess->host->realm) != 0)
557  {
558  log_debug(ZONE, "user create failed");
560  return;
561  }
562 
563  else
564  log_write(c2s->log, LOG_NOTICE, "[%d] created user: user=%s; realm=%s", sess->s->tag, username, sess->host->realm);
565 
566  /* extract the password */
567  snprintf(password, 257, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
568 
569  /* change it */
570  if((c2s->ar->set_password)(c2s->ar, username, sess->host->realm, password) != 0)
571  {
572  log_debug(ZONE, "password store failed");
574  return;
575  }
576 
577  log_debug(ZONE, "updated auth creds for %s", username);
578 
579  /* make a result nad */
580  sess->result = nad_new();
581 
582  ns = nad_add_namespace(sess->result, uri_CLIENT, NULL);
583 
584  nad_append_elem(sess->result, ns, "iq", 0);
585  nad_set_attr(sess->result, 0, -1, "type", "result", 6);
586 
587  /* extract the id */
588  attr = nad_find_attr(nad, 0, -1, "id", NULL);
589  if(attr >= 0)
590  nad_set_attr(sess->result, 0, -1, "id", NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
591 
592  /* if they're active, then this was just a password change, and we're done */
593  if(sess->active) {
594  log_write(c2s->log, LOG_NOTICE, "[%d] password changed: jid=%s", sess->s->tag, jid_user(sess->resources->jid));
595  sx_nad_write(sess->s, sess->result);
596  sess->result = NULL;
597  return;
598  }
599 
600  /* create new bound jid holder */
601  if(sess->resources == NULL) {
602  sess->resources = (bres_t) calloc(1, sizeof(struct bres_st));
603  }
604 
605  /* our local id */
606  sprintf(sess->resources->c2s_id, "%d", sess->s->tag);
607 
608  /* the user jid for this transaction */
609  sess->resources->jid = jid_new(sess->s->req_to, -1);
610  jid_reset_components(sess->resources->jid, username, sess->resources->jid->domain, sess->resources->jid->resource);
611 
612  log_write(c2s->log, LOG_NOTICE, "[%d] registration succeeded, requesting user creation: jid=%s", sess->s->tag, jid_user(sess->resources->jid));
613 
614  /* get the sm to create them */
615  sm_create(sess, sess->resources);
616 
617  nad_free(nad);
618 
619  return;
620 }
621 
626 int authreg_process(c2s_t c2s, sess_t sess, nad_t nad) {
627  int ns, query, type, authreg = -1, getset = -1;
628 
629  /* need iq */
630  if(NAD_ENAME_L(nad, 0) != 2 || strncmp("iq", NAD_ENAME(nad, 0), 2) != 0)
631  return 1;
632 
633  /* only want auth or register packets */
634  if((ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL)) >= 0 && (query = nad_find_elem(nad, 0, ns, "query", 1)) >= 0)
635  authreg = 0;
636  else if((ns = nad_find_scoped_namespace(nad, uri_REGISTER, NULL)) >= 0 && (query = nad_find_elem(nad, 0, ns, "query", 1)) >= 0)
637  authreg = 1;
638  else
639  return 1;
640 
641  /* if its to someone else, pass it */
642  if(nad_find_attr(nad, 0, -1, "to", NULL) >= 0 && nad_find_attr(nad, 0, -1, "to", sess->s->req_to) < 0)
643  return 1;
644 
645  /* need a type */
646  if((type = nad_find_attr(nad, 0, -1, "type", NULL)) < 0 || NAD_AVAL_L(nad, type) != 3)
647  {
649  return 0;
650  }
651 
652  /* get or set? */
653  if(strncmp("get", NAD_AVAL(nad, type), NAD_AVAL_L(nad, type)) == 0)
654  getset = 0;
655  else if(strncmp("set", NAD_AVAL(nad, type), NAD_AVAL_L(nad, type)) == 0)
656  getset = 1;
657  else
658  {
660  return 0;
661  }
662 
663  /* hand to the correct handler */
664  if(authreg == 0) {
665  /* can't do iq:auth after sasl auth */
666  if(sess->sasl_authd) {
668  return 0;
669  }
670 
671  if(getset == 0) {
672  log_debug(ZONE, "auth get");
673  _authreg_auth_get(c2s, sess, nad);
674  } else if(getset == 1) {
675  log_debug(ZONE, "auth set");
676  _authreg_auth_set(c2s, sess, nad);
677  }
678  }
679 
680  if(authreg == 1) {
681  if(getset == 0) {
682  log_debug(ZONE, "register get");
683  _authreg_register_get(c2s, sess, nad);
684  } else if(getset == 1) {
685  log_debug(ZONE, "register set");
686  _authreg_register_set(c2s, sess, nad);
687  }
688  }
689 
690  /* handled */
691  return 0;
692 }