• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/ntvfs/unixuid/
1/*
2   Unix SMB/CIFS implementation.
3
4   a pass-thru NTVFS module to setup a security context using unix
5   uid/gid
6
7   Copyright (C) Andrew Tridgell 2004
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "includes.h"
24#include "system/filesys.h"
25#include "system/passwd.h"
26#include "auth/auth.h"
27#include "ntvfs/ntvfs.h"
28#include "libcli/wbclient/wbclient.h"
29#define TEVENT_DEPRECATED
30#include <tevent.h>
31
32struct unixuid_private {
33	struct wbc_context *wbc_ctx;
34	struct unix_sec_ctx *last_sec_ctx;
35	struct security_token *last_token;
36};
37
38
39
40struct unix_sec_ctx {
41	uid_t uid;
42	gid_t gid;
43	uint_t ngroups;
44	gid_t *groups;
45};
46
47/*
48  pull the current security context into a unix_sec_ctx
49*/
50static struct unix_sec_ctx *save_unix_security(TALLOC_CTX *mem_ctx)
51{
52	struct unix_sec_ctx *sec = talloc(mem_ctx, struct unix_sec_ctx);
53	if (sec == NULL) {
54		return NULL;
55	}
56	sec->uid = geteuid();
57	sec->gid = getegid();
58	sec->ngroups = getgroups(0, NULL);
59	if (sec->ngroups == -1) {
60		talloc_free(sec);
61		return NULL;
62	}
63	sec->groups = talloc_array(sec, gid_t, sec->ngroups);
64	if (sec->groups == NULL) {
65		talloc_free(sec);
66		return NULL;
67	}
68
69	if (getgroups(sec->ngroups, sec->groups) != sec->ngroups) {
70		talloc_free(sec);
71		return NULL;
72	}
73
74	return sec;
75}
76
77/*
78  set the current security context from a unix_sec_ctx
79*/
80static NTSTATUS set_unix_security(struct unix_sec_ctx *sec)
81{
82	seteuid(0);
83
84	if (setgroups(sec->ngroups, sec->groups) != 0) {
85		return NT_STATUS_ACCESS_DENIED;
86	}
87	if (setegid(sec->gid) != 0) {
88		return NT_STATUS_ACCESS_DENIED;
89	}
90	if (seteuid(sec->uid) != 0) {
91		return NT_STATUS_ACCESS_DENIED;
92	}
93	return NT_STATUS_OK;
94}
95
96static int unixuid_nesting_level;
97
98/*
99  called at the start and end of a tevent nesting loop. Needs to save/restore
100  unix security context
101 */
102static int unixuid_event_nesting_hook(struct tevent_context *ev,
103				      void *private_data,
104				      uint32_t level,
105				      bool begin,
106				      void *stack_ptr,
107				      const char *location)
108{
109	struct unix_sec_ctx *sec_ctx;
110
111	if (unixuid_nesting_level == 0) {
112		/* we don't need to do anything unless we are nested
113		   inside of a call in this module */
114		return 0;
115	}
116
117	if (begin) {
118		sec_ctx = save_unix_security(ev);
119		if (sec_ctx == NULL) {
120			DEBUG(0,("%s: Failed to save security context\n", location));
121			return -1;
122		}
123		*(struct unix_sec_ctx **)stack_ptr = sec_ctx;
124		if (seteuid(0) != 0 || setegid(0) != 0) {
125			DEBUG(0,("%s: Failed to change to root\n", location));
126			return -1;
127		}
128	} else {
129		/* called when we come out of a nesting level */
130		NTSTATUS status;
131
132		sec_ctx = *(struct unix_sec_ctx **)stack_ptr;
133		if (sec_ctx == NULL) {
134			/* this happens the first time this function
135			   is called, as we install the hook while
136			   inside an event in unixuid_connect() */
137			return 0;
138		}
139
140		sec_ctx = talloc_get_type_abort(sec_ctx, struct unix_sec_ctx);
141		status = set_unix_security(sec_ctx);
142		talloc_free(sec_ctx);
143		if (!NT_STATUS_IS_OK(status)) {
144			DEBUG(0,("%s: Failed to revert security context (%s)\n",
145				 location, nt_errstr(status)));
146			return -1;
147		}
148	}
149
150	return 0;
151}
152
153
154/*
155  form a unix_sec_ctx from the current security_token
156*/
157static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
158					  struct ntvfs_request *req,
159					  struct security_token *token,
160					  struct unix_sec_ctx **sec)
161{
162	struct unixuid_private *priv = ntvfs->private_data;
163	int i;
164	NTSTATUS status;
165	struct id_mapping *ids;
166	struct composite_context *ctx;
167	*sec = talloc(req, struct unix_sec_ctx);
168
169	/* we can't do unix security without a user and group */
170	if (token->num_sids < 2) {
171		return NT_STATUS_ACCESS_DENIED;
172	}
173
174	ids = talloc_array(req, struct id_mapping, token->num_sids);
175	NT_STATUS_HAVE_NO_MEMORY(ids);
176
177	ids[0].unixid = NULL;
178	ids[0].sid = token->user_sid;
179	ids[0].status = NT_STATUS_NONE_MAPPED;
180
181	ids[1].unixid = NULL;
182	ids[1].sid = token->group_sid;
183	ids[1].status = NT_STATUS_NONE_MAPPED;
184
185	(*sec)->ngroups = token->num_sids - 2;
186	(*sec)->groups = talloc_array(*sec, gid_t, (*sec)->ngroups);
187	NT_STATUS_HAVE_NO_MEMORY((*sec)->groups);
188
189	for (i=0;i<(*sec)->ngroups;i++) {
190		ids[i+2].unixid = NULL;
191		ids[i+2].sid = token->sids[i+2];
192		ids[i+2].status = NT_STATUS_NONE_MAPPED;
193	}
194
195	ctx = wbc_sids_to_xids_send(priv->wbc_ctx, ids, token->num_sids, ids);
196	NT_STATUS_HAVE_NO_MEMORY(ctx);
197
198	status = wbc_sids_to_xids_recv(ctx, &ids);
199	NT_STATUS_NOT_OK_RETURN(status);
200
201	if (ids[0].unixid->type == ID_TYPE_BOTH ||
202	    ids[0].unixid->type == ID_TYPE_UID) {
203		(*sec)->uid = ids[0].unixid->id;
204	} else {
205		return NT_STATUS_INVALID_SID;
206	}
207
208	if (ids[1].unixid->type == ID_TYPE_BOTH ||
209	    ids[1].unixid->type == ID_TYPE_GID) {
210		(*sec)->gid = ids[1].unixid->id;
211	} else {
212		return NT_STATUS_INVALID_SID;
213	}
214
215	for (i=0;i<(*sec)->ngroups;i++) {
216		if (ids[i+2].unixid->type == ID_TYPE_BOTH ||
217		    ids[i+2].unixid->type == ID_TYPE_GID) {
218			(*sec)->groups[i] = ids[i+2].unixid->id;
219		} else {
220			return NT_STATUS_INVALID_SID;
221		}
222	}
223
224	return NT_STATUS_OK;
225}
226
227/*
228  setup our unix security context according to the session authentication info
229*/
230static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
231				       struct ntvfs_request *req, struct unix_sec_ctx **sec)
232{
233	struct unixuid_private *priv = ntvfs->private_data;
234	struct security_token *token;
235	struct unix_sec_ctx *newsec;
236	NTSTATUS status;
237
238	if (req->session_info == NULL) {
239		return NT_STATUS_ACCESS_DENIED;
240	}
241
242	token = req->session_info->security_token;
243
244	*sec = save_unix_security(ntvfs);
245	if (*sec == NULL) {
246		return NT_STATUS_NO_MEMORY;
247	}
248
249	if (token == priv->last_token) {
250		newsec = priv->last_sec_ctx;
251	} else {
252		status = nt_token_to_unix_security(ntvfs, req, token, &newsec);
253		if (!NT_STATUS_IS_OK(status)) {
254			talloc_free(*sec);
255			return status;
256		}
257		if (priv->last_sec_ctx) {
258			talloc_free(priv->last_sec_ctx);
259		}
260		priv->last_sec_ctx = newsec;
261		priv->last_token = token;
262		talloc_steal(priv, newsec);
263	}
264
265	status = set_unix_security(newsec);
266	if (!NT_STATUS_IS_OK(status)) {
267		talloc_free(*sec);
268		return status;
269	}
270
271	return NT_STATUS_OK;
272}
273
274/*
275  this pass through macro operates on request contexts
276*/
277#define PASS_THRU_REQ(ntvfs, req, op, args) do { \
278	NTSTATUS status2; \
279	struct unix_sec_ctx *sec; \
280	status = unixuid_setup_security(ntvfs, req, &sec); \
281	NT_STATUS_NOT_OK_RETURN(status); \
282	unixuid_nesting_level++; \
283	status = ntvfs_next_##op args; \
284	unixuid_nesting_level--; \
285	status2 = set_unix_security(sec); \
286	talloc_free(sec); \
287	if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
288} while (0)
289
290
291
292/*
293  connect to a share - used when a tree_connect operation comes in.
294*/
295static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
296				struct ntvfs_request *req, union smb_tcon *tcon)
297{
298	struct unixuid_private *priv;
299	NTSTATUS status;
300
301	priv = talloc(ntvfs, struct unixuid_private);
302	if (!priv) {
303		return NT_STATUS_NO_MEMORY;
304	}
305
306	priv->wbc_ctx = wbc_init(priv, ntvfs->ctx->msg_ctx,
307				    ntvfs->ctx->event_ctx);
308	if (priv->wbc_ctx == NULL) {
309		talloc_free(priv);
310		return NT_STATUS_INTERNAL_ERROR;
311	}
312
313	priv->last_sec_ctx = NULL;
314	priv->last_token = NULL;
315	ntvfs->private_data = priv;
316
317	tevent_loop_set_nesting_hook(ntvfs->ctx->event_ctx,
318				     unixuid_event_nesting_hook,
319				     &unixuid_nesting_level);
320
321	/* we don't use PASS_THRU_REQ here, as the connect operation runs with
322	   root privileges. This allows the backends to setup any database
323	   links they might need during the connect. */
324	status = ntvfs_next_connect(ntvfs, req, tcon);
325
326	return status;
327}
328
329/*
330  disconnect from a share
331*/
332static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs)
333{
334	struct unixuid_private *priv = ntvfs->private_data;
335	NTSTATUS status;
336
337	talloc_free(priv);
338	ntvfs->private_data = NULL;
339
340	status = ntvfs_next_disconnect(ntvfs);
341
342	return status;
343}
344
345
346/*
347  delete a file
348*/
349static NTSTATUS unixuid_unlink(struct ntvfs_module_context *ntvfs,
350			      struct ntvfs_request *req,
351			      union smb_unlink *unl)
352{
353	NTSTATUS status;
354
355	PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, req, unl));
356
357	return status;
358}
359
360/*
361  ioctl interface
362*/
363static NTSTATUS unixuid_ioctl(struct ntvfs_module_context *ntvfs,
364			     struct ntvfs_request *req, union smb_ioctl *io)
365{
366	NTSTATUS status;
367
368	PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io));
369
370	return status;
371}
372
373/*
374  check if a directory exists
375*/
376static NTSTATUS unixuid_chkpath(struct ntvfs_module_context *ntvfs,
377			        struct ntvfs_request *req,
378				union smb_chkpath *cp)
379{
380	NTSTATUS status;
381
382	PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, req, cp));
383
384	return status;
385}
386
387/*
388  return info on a pathname
389*/
390static NTSTATUS unixuid_qpathinfo(struct ntvfs_module_context *ntvfs,
391				 struct ntvfs_request *req, union smb_fileinfo *info)
392{
393	NTSTATUS status;
394
395	PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, req, info));
396
397	return status;
398}
399
400/*
401  query info on a open file
402*/
403static NTSTATUS unixuid_qfileinfo(struct ntvfs_module_context *ntvfs,
404				 struct ntvfs_request *req, union smb_fileinfo *info)
405{
406	NTSTATUS status;
407
408	PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, req, info));
409
410	return status;
411}
412
413
414/*
415  set info on a pathname
416*/
417static NTSTATUS unixuid_setpathinfo(struct ntvfs_module_context *ntvfs,
418				   struct ntvfs_request *req, union smb_setfileinfo *st)
419{
420	NTSTATUS status;
421
422	PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, req, st));
423
424	return status;
425}
426
427/*
428  open a file
429*/
430static NTSTATUS unixuid_open(struct ntvfs_module_context *ntvfs,
431			     struct ntvfs_request *req, union smb_open *io)
432{
433	NTSTATUS status;
434
435	PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io));
436
437	return status;
438}
439
440/*
441  create a directory
442*/
443static NTSTATUS unixuid_mkdir(struct ntvfs_module_context *ntvfs,
444			     struct ntvfs_request *req, union smb_mkdir *md)
445{
446	NTSTATUS status;
447
448	PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md));
449
450	return status;
451}
452
453/*
454  remove a directory
455*/
456static NTSTATUS unixuid_rmdir(struct ntvfs_module_context *ntvfs,
457			     struct ntvfs_request *req, struct smb_rmdir *rd)
458{
459	NTSTATUS status;
460
461	PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, req, rd));
462
463	return status;
464}
465
466/*
467  rename a set of files
468*/
469static NTSTATUS unixuid_rename(struct ntvfs_module_context *ntvfs,
470			      struct ntvfs_request *req, union smb_rename *ren)
471{
472	NTSTATUS status;
473
474	PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, req, ren));
475
476	return status;
477}
478
479/*
480  copy a set of files
481*/
482static NTSTATUS unixuid_copy(struct ntvfs_module_context *ntvfs,
483			    struct ntvfs_request *req, struct smb_copy *cp)
484{
485	NTSTATUS status;
486
487	PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp));
488
489	return status;
490}
491
492/*
493  read from a file
494*/
495static NTSTATUS unixuid_read(struct ntvfs_module_context *ntvfs,
496			    struct ntvfs_request *req, union smb_read *rd)
497{
498	NTSTATUS status;
499
500	PASS_THRU_REQ(ntvfs, req, read, (ntvfs, req, rd));
501
502	return status;
503}
504
505/*
506  write to a file
507*/
508static NTSTATUS unixuid_write(struct ntvfs_module_context *ntvfs,
509			     struct ntvfs_request *req, union smb_write *wr)
510{
511	NTSTATUS status;
512
513	PASS_THRU_REQ(ntvfs, req, write, (ntvfs, req, wr));
514
515	return status;
516}
517
518/*
519  seek in a file
520*/
521static NTSTATUS unixuid_seek(struct ntvfs_module_context *ntvfs,
522			     struct ntvfs_request *req,
523			     union smb_seek *io)
524{
525	NTSTATUS status;
526
527	PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io));
528
529	return status;
530}
531
532/*
533  flush a file
534*/
535static NTSTATUS unixuid_flush(struct ntvfs_module_context *ntvfs,
536			      struct ntvfs_request *req,
537			      union smb_flush *io)
538{
539	NTSTATUS status;
540
541	PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, req, io));
542
543	return status;
544}
545
546/*
547  close a file
548*/
549static NTSTATUS unixuid_close(struct ntvfs_module_context *ntvfs,
550			     struct ntvfs_request *req, union smb_close *io)
551{
552	NTSTATUS status;
553
554	PASS_THRU_REQ(ntvfs, req, close, (ntvfs, req, io));
555
556	return status;
557}
558
559/*
560  exit - closing files
561*/
562static NTSTATUS unixuid_exit(struct ntvfs_module_context *ntvfs,
563			    struct ntvfs_request *req)
564{
565	NTSTATUS status;
566
567	PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req));
568
569	return status;
570}
571
572/*
573  logoff - closing files
574*/
575static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
576			      struct ntvfs_request *req)
577{
578	struct unixuid_private *priv = ntvfs->private_data;
579	NTSTATUS status;
580
581	PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
582
583	priv->last_token = NULL;
584
585	return status;
586}
587
588/*
589  async setup
590*/
591static NTSTATUS unixuid_async_setup(struct ntvfs_module_context *ntvfs,
592				    struct ntvfs_request *req,
593				    void *private_data)
594{
595	NTSTATUS status;
596
597	PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private_data));
598
599	return status;
600}
601
602/*
603  cancel an async request
604*/
605static NTSTATUS unixuid_cancel(struct ntvfs_module_context *ntvfs,
606			       struct ntvfs_request *req)
607{
608	NTSTATUS status;
609
610	PASS_THRU_REQ(ntvfs, req, cancel, (ntvfs, req));
611
612	return status;
613}
614
615/*
616  change notify
617*/
618static NTSTATUS unixuid_notify(struct ntvfs_module_context *ntvfs,
619			       struct ntvfs_request *req, union smb_notify *info)
620{
621	NTSTATUS status;
622
623	PASS_THRU_REQ(ntvfs, req, notify, (ntvfs, req, info));
624
625	return status;
626}
627
628/*
629  lock a byte range
630*/
631static NTSTATUS unixuid_lock(struct ntvfs_module_context *ntvfs,
632			    struct ntvfs_request *req, union smb_lock *lck)
633{
634	NTSTATUS status;
635
636	PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, req, lck));
637
638	return status;
639}
640
641/*
642  set info on a open file
643*/
644static NTSTATUS unixuid_setfileinfo(struct ntvfs_module_context *ntvfs,
645				   struct ntvfs_request *req,
646				   union smb_setfileinfo *info)
647{
648	NTSTATUS status;
649
650	PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info));
651
652	return status;
653}
654
655
656/*
657  return filesystem space info
658*/
659static NTSTATUS unixuid_fsinfo(struct ntvfs_module_context *ntvfs,
660			      struct ntvfs_request *req, union smb_fsinfo *fs)
661{
662	NTSTATUS status;
663
664	PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs));
665
666	return status;
667}
668
669/*
670  return print queue info
671*/
672static NTSTATUS unixuid_lpq(struct ntvfs_module_context *ntvfs,
673			   struct ntvfs_request *req, union smb_lpq *lpq)
674{
675	NTSTATUS status;
676
677	PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, req, lpq));
678
679	return status;
680}
681
682/*
683   list files in a directory matching a wildcard pattern
684*/
685static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
686				    struct ntvfs_request *req, union smb_search_first *io,
687				    void *search_private,
688				    bool (*callback)(void *, const union smb_search_data *))
689{
690	NTSTATUS status;
691
692	PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback));
693
694	return status;
695}
696
697/* continue a search */
698static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
699				   struct ntvfs_request *req, union smb_search_next *io,
700				   void *search_private,
701				   bool (*callback)(void *, const union smb_search_data *))
702{
703	NTSTATUS status;
704
705	PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, req, io, search_private, callback));
706
707	return status;
708}
709
710/* close a search */
711static NTSTATUS unixuid_search_close(struct ntvfs_module_context *ntvfs,
712				    struct ntvfs_request *req, union smb_search_close *io)
713{
714	NTSTATUS status;
715
716	PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, req, io));
717
718	return status;
719}
720
721/* SMBtrans - not used on file shares */
722static NTSTATUS unixuid_trans(struct ntvfs_module_context *ntvfs,
723			     struct ntvfs_request *req, struct smb_trans2 *trans2)
724{
725	NTSTATUS status;
726
727	PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, req, trans2));
728
729	return status;
730}
731
732/*
733  initialise the unixuid backend, registering ourselves with the ntvfs subsystem
734 */
735NTSTATUS ntvfs_unixuid_init(void)
736{
737	NTSTATUS ret;
738	struct ntvfs_ops ops;
739	NTVFS_CURRENT_CRITICAL_SIZES(vers);
740
741	ZERO_STRUCT(ops);
742
743	/* fill in all the operations */
744	ops.connect = unixuid_connect;
745	ops.disconnect = unixuid_disconnect;
746	ops.unlink = unixuid_unlink;
747	ops.chkpath = unixuid_chkpath;
748	ops.qpathinfo = unixuid_qpathinfo;
749	ops.setpathinfo = unixuid_setpathinfo;
750	ops.open = unixuid_open;
751	ops.mkdir = unixuid_mkdir;
752	ops.rmdir = unixuid_rmdir;
753	ops.rename = unixuid_rename;
754	ops.copy = unixuid_copy;
755	ops.ioctl = unixuid_ioctl;
756	ops.read = unixuid_read;
757	ops.write = unixuid_write;
758	ops.seek = unixuid_seek;
759	ops.flush = unixuid_flush;
760	ops.close = unixuid_close;
761	ops.exit = unixuid_exit;
762	ops.lock = unixuid_lock;
763	ops.setfileinfo = unixuid_setfileinfo;
764	ops.qfileinfo = unixuid_qfileinfo;
765	ops.fsinfo = unixuid_fsinfo;
766	ops.lpq = unixuid_lpq;
767	ops.search_first = unixuid_search_first;
768	ops.search_next = unixuid_search_next;
769	ops.search_close = unixuid_search_close;
770	ops.trans = unixuid_trans;
771	ops.logoff = unixuid_logoff;
772	ops.async_setup = unixuid_async_setup;
773	ops.cancel = unixuid_cancel;
774	ops.notify = unixuid_notify;
775
776	ops.name = "unixuid";
777
778	/* we register under all 3 backend types, as we are not type specific */
779	ops.type = NTVFS_DISK;
780	ret = ntvfs_register(&ops, &vers);
781	if (!NT_STATUS_IS_OK(ret)) goto failed;
782
783	ops.type = NTVFS_PRINT;
784	ret = ntvfs_register(&ops, &vers);
785	if (!NT_STATUS_IS_OK(ret)) goto failed;
786
787	ops.type = NTVFS_IPC;
788	ret = ntvfs_register(&ops, &vers);
789	if (!NT_STATUS_IS_OK(ret)) goto failed;
790
791failed:
792	return ret;
793}
794