1/*
2 *  Unix SMB/CIFS implementation.
3 *  RPC Pipe client / server routines
4 *  Copyright (C) Andrew Tridgell              1992-2000,
5 *  Copyright (C) Jean Fran�ois Micouleau      1998-2001.
6 *  Copyright (C) Volker Lendecke              2006.
7 *  Copyright (C) Gerald Carter                2006.
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 2 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, write to the Free Software
21 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include "includes.h"
25#include "groupdb/mapping.h"
26
27/****************************************************************************
28initialise first time the mapping list
29****************************************************************************/
30NTSTATUS add_initial_entry(gid_t gid, const char *sid, enum lsa_SidType sid_name_use, const char *nt_name, const char *comment)
31{
32	GROUP_MAP map;
33
34	if(!init_group_mapping()) {
35		DEBUG(0,("failed to initialize group mapping\n"));
36		return NT_STATUS_UNSUCCESSFUL;
37	}
38
39	map.gid=gid;
40	if (!string_to_sid(&map.sid, sid)) {
41		DEBUG(0, ("string_to_sid failed: %s", sid));
42		return NT_STATUS_UNSUCCESSFUL;
43	}
44
45	map.sid_name_use=sid_name_use;
46	fstrcpy(map.nt_name, nt_name);
47	fstrcpy(map.comment, comment);
48
49	return pdb_add_group_mapping_entry(&map);
50}
51
52/****************************************************************************
53 Map a unix group to a newly created mapping
54****************************************************************************/
55NTSTATUS map_unix_group(const struct group *grp, GROUP_MAP *pmap)
56{
57	NTSTATUS status;
58	GROUP_MAP map;
59	const char *grpname, *dom, *name;
60	uint32 rid;
61
62	if (pdb_getgrgid(&map, grp->gr_gid)) {
63		return NT_STATUS_GROUP_EXISTS;
64	}
65
66	map.gid = grp->gr_gid;
67	grpname = grp->gr_name;
68
69	if (lookup_name(tmp_talloc_ctx(), grpname, LOOKUP_NAME_ISOLATED,
70			&dom, &name, NULL, NULL)) {
71
72		const char *tmp = talloc_asprintf(
73			tmp_talloc_ctx(), "Unix Group %s", grp->gr_name);
74
75		DEBUG(5, ("%s exists as %s\\%s, retrying as \"%s\"\n",
76			  grpname, dom, name, tmp));
77		grpname = tmp;
78	}
79
80	if (lookup_name(tmp_talloc_ctx(), grpname, LOOKUP_NAME_ISOLATED,
81			NULL, NULL, NULL, NULL)) {
82		DEBUG(3, ("\"%s\" exists, can't map it\n", grp->gr_name));
83		return NT_STATUS_GROUP_EXISTS;
84	}
85
86	fstrcpy(map.nt_name, grpname);
87
88	if (pdb_rid_algorithm()) {
89		rid = algorithmic_pdb_gid_to_group_rid( grp->gr_gid );
90	} else {
91		if (!pdb_new_rid(&rid)) {
92			DEBUG(3, ("Could not get a new RID for %s\n",
93				  grp->gr_name));
94			return NT_STATUS_ACCESS_DENIED;
95		}
96	}
97
98	sid_compose(&map.sid, get_global_sam_sid(), rid);
99	map.sid_name_use = SID_NAME_DOM_GRP;
100	fstrcpy(map.comment, talloc_asprintf(tmp_talloc_ctx(), "Unix Group %s",
101					     grp->gr_name));
102
103	status = pdb_add_group_mapping_entry(&map);
104	if (NT_STATUS_IS_OK(status)) {
105		*pmap = map;
106	}
107	return status;
108}
109
110
111
112
113
114
115
116static NTSTATUS alias_memberships(const DOM_SID *members, size_t num_members,
117				  DOM_SID **sids, size_t *num)
118{
119	size_t i;
120
121	*num = 0;
122	*sids = NULL;
123
124	for (i=0; i<num_members; i++) {
125		NTSTATUS status = one_alias_membership(&members[i], sids, num);
126		if (!NT_STATUS_IS_OK(status))
127			return status;
128	}
129	return NT_STATUS_OK;
130}
131
132struct aliasmem_closure {
133	const DOM_SID *alias;
134	DOM_SID **sids;
135	size_t *num;
136};
137
138
139
140/*
141 *
142 * High level functions
143 * better to use them than the lower ones.
144 *
145 * we are checking if the group is in the mapping file
146 * and if the group is an existing unix group
147 *
148 */
149
150/* get a domain group from it's SID */
151
152BOOL get_domain_group_from_sid(DOM_SID sid, GROUP_MAP *map)
153{
154	struct group *grp;
155	BOOL ret;
156
157	if(!init_group_mapping()) {
158		DEBUG(0,("failed to initialize group mapping\n"));
159		return(False);
160	}
161
162	DEBUG(10, ("get_domain_group_from_sid\n"));
163
164	/* if the group is NOT in the database, it CAN NOT be a domain group */
165
166	become_root();
167	ret = pdb_getgrsid(map, sid);
168	unbecome_root();
169
170	/* special case check for rid 513 */
171
172	if ( !ret ) {
173		uint32 rid;
174
175		sid_peek_rid( &sid, &rid );
176
177		if ( rid == DOMAIN_GROUP_RID_USERS ) {
178			fstrcpy( map->nt_name, "None" );
179			fstrcpy( map->comment, "Ordinary Users" );
180			sid_copy( &map->sid, &sid );
181			map->sid_name_use = SID_NAME_DOM_GRP;
182
183			return True;
184		}
185
186		return False;
187	}
188
189	DEBUG(10, ("get_domain_group_from_sid: SID found in the TDB\n"));
190
191	/* if it's not a domain group, continue */
192	if (map->sid_name_use!=SID_NAME_DOM_GRP) {
193		return False;
194	}
195
196	DEBUG(10, ("get_domain_group_from_sid: SID is a domain group\n"));
197
198	if (map->gid==-1) {
199		return False;
200	}
201
202	DEBUG(10, ("get_domain_group_from_sid: SID is mapped to gid:%lu\n",(unsigned long)map->gid));
203
204	grp = getgrgid(map->gid);
205	if ( !grp ) {
206		DEBUG(10, ("get_domain_group_from_sid: gid DOESN'T exist in UNIX security\n"));
207		return False;
208	}
209
210	DEBUG(10, ("get_domain_group_from_sid: gid exists in UNIX security\n"));
211
212	return True;
213}
214
215/****************************************************************************
216 Create a UNIX group on demand.
217****************************************************************************/
218
219int smb_create_group(const char *unix_group, gid_t *new_gid)
220{
221	pstring add_script;
222	int 	ret = -1;
223	int 	fd = 0;
224
225	*new_gid = 0;
226
227	/* defer to scripts */
228
229	if ( *lp_addgroup_script() ) {
230		pstrcpy(add_script, lp_addgroup_script());
231		pstring_sub(add_script, "%g", unix_group);
232		ret = smbrun(add_script, &fd);
233		DEBUG(ret ? 0 : 3,("smb_create_group: Running the command `%s' gave %d\n",add_script,ret));
234		if (ret == 0) {
235			smb_nscd_flush_group_cache();
236		}
237		if (ret != 0)
238			return ret;
239
240		if (fd != 0) {
241			fstring output;
242
243			*new_gid = 0;
244			if (read(fd, output, sizeof(output)) > 0) {
245				*new_gid = (gid_t)strtoul(output, NULL, 10);
246			}
247
248			close(fd);
249		}
250
251	}
252
253	if (*new_gid == 0) {
254		struct group *grp = getgrnam(unix_group);
255
256		if (grp != NULL)
257			*new_gid = grp->gr_gid;
258	}
259
260	return ret;
261}
262
263/****************************************************************************
264 Delete a UNIX group on demand.
265****************************************************************************/
266
267int smb_delete_group(const char *unix_group)
268{
269	pstring del_script;
270	int ret;
271
272	/* defer to scripts */
273
274	if ( *lp_delgroup_script() ) {
275		pstrcpy(del_script, lp_delgroup_script());
276		pstring_sub(del_script, "%g", unix_group);
277		ret = smbrun(del_script,NULL);
278		DEBUG(ret ? 0 : 3,("smb_delete_group: Running the command `%s' gave %d\n",del_script,ret));
279		if (ret == 0) {
280			smb_nscd_flush_group_cache();
281		}
282		return ret;
283	}
284
285	return -1;
286}
287
288/****************************************************************************
289 Set a user's primary UNIX group.
290****************************************************************************/
291int smb_set_primary_group(const char *unix_group, const char* unix_user)
292{
293	pstring add_script;
294	int ret;
295
296	/* defer to scripts */
297
298	if ( *lp_setprimarygroup_script() ) {
299		pstrcpy(add_script, lp_setprimarygroup_script());
300		all_string_sub(add_script, "%g", unix_group, sizeof(add_script));
301		all_string_sub(add_script, "%u", unix_user, sizeof(add_script));
302		ret = smbrun(add_script,NULL);
303		flush_pwnam_cache();
304		DEBUG(ret ? 0 : 3,("smb_set_primary_group: "
305			 "Running the command `%s' gave %d\n",add_script,ret));
306		if (ret == 0) {
307			smb_nscd_flush_group_cache();
308		}
309		return ret;
310	}
311
312	return -1;
313}
314
315/****************************************************************************
316 Add a user to a UNIX group.
317****************************************************************************/
318
319int smb_add_user_group(const char *unix_group, const char *unix_user)
320{
321	pstring add_script;
322	int ret;
323
324	/* defer to scripts */
325
326	if ( *lp_addusertogroup_script() ) {
327		pstrcpy(add_script, lp_addusertogroup_script());
328		pstring_sub(add_script, "%g", unix_group);
329		pstring_sub(add_script, "%u", unix_user);
330		ret = smbrun(add_script,NULL);
331		DEBUG(ret ? 0 : 3,("smb_add_user_group: Running the command `%s' gave %d\n",add_script,ret));
332		if (ret == 0) {
333			smb_nscd_flush_group_cache();
334		}
335		return ret;
336	}
337
338	return -1;
339}
340
341/****************************************************************************
342 Delete a user from a UNIX group
343****************************************************************************/
344
345int smb_delete_user_group(const char *unix_group, const char *unix_user)
346{
347	pstring del_script;
348	int ret;
349
350	/* defer to scripts */
351
352	if ( *lp_deluserfromgroup_script() ) {
353		pstrcpy(del_script, lp_deluserfromgroup_script());
354		pstring_sub(del_script, "%g", unix_group);
355		pstring_sub(del_script, "%u", unix_user);
356		ret = smbrun(del_script,NULL);
357		DEBUG(ret ? 0 : 3,("smb_delete_user_group: Running the command `%s' gave %d\n",del_script,ret));
358		if (ret == 0) {
359			smb_nscd_flush_group_cache();
360		}
361		return ret;
362	}
363
364	return -1;
365}
366
367
368NTSTATUS pdb_default_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
369				 DOM_SID sid)
370{
371	return get_group_map_from_sid(sid, map) ?
372		NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
373}
374
375NTSTATUS pdb_default_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
376				 gid_t gid)
377{
378	return get_group_map_from_gid(gid, map) ?
379		NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
380}
381
382NTSTATUS pdb_default_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
383				 const char *name)
384{
385	return get_group_map_from_ntname(name, map) ?
386		NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
387}
388
389NTSTATUS pdb_default_add_group_mapping_entry(struct pdb_methods *methods,
390						GROUP_MAP *map)
391{
392	return add_mapping_entry(map, TDB_INSERT) ?
393		NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
394}
395
396NTSTATUS pdb_default_update_group_mapping_entry(struct pdb_methods *methods,
397						   GROUP_MAP *map)
398{
399	return add_mapping_entry(map, TDB_REPLACE) ?
400		NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
401}
402
403NTSTATUS pdb_default_delete_group_mapping_entry(struct pdb_methods *methods,
404						   DOM_SID sid)
405{
406	return group_map_remove(&sid) ?
407		NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
408}
409
410NTSTATUS pdb_default_enum_group_mapping(struct pdb_methods *methods,
411					   const DOM_SID *sid, enum lsa_SidType sid_name_use,
412					   GROUP_MAP **pp_rmap, size_t *p_num_entries,
413					   BOOL unix_only)
414{
415	return enum_group_mapping(sid, sid_name_use, pp_rmap, p_num_entries, unix_only) ?
416		NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
417}
418
419NTSTATUS pdb_default_find_alias(struct pdb_methods *methods,
420				const char *name, DOM_SID *sid)
421{
422	GROUP_MAP map;
423
424	if (!pdb_getgrnam(&map, name))
425		return NT_STATUS_NO_SUCH_ALIAS;
426
427	if ((map.sid_name_use != SID_NAME_WKN_GRP) &&
428	    (map.sid_name_use != SID_NAME_ALIAS))
429		return NT_STATUS_OBJECT_TYPE_MISMATCH;
430
431	sid_copy(sid, &map.sid);
432	return NT_STATUS_OK;
433}
434
435NTSTATUS pdb_default_create_alias(struct pdb_methods *methods,
436				  const char *name, uint32 *rid)
437{
438	DOM_SID sid;
439	enum lsa_SidType type;
440	uint32 new_rid;
441	gid_t gid;
442	BOOL exists;
443	GROUP_MAP map;
444	TALLOC_CTX *mem_ctx;
445	NTSTATUS status;
446
447	DEBUG(10, ("Trying to create alias %s\n", name));
448
449	mem_ctx = talloc_new(NULL);
450	if (mem_ctx == NULL) {
451		return NT_STATUS_NO_MEMORY;
452	}
453
454	exists = lookup_name(mem_ctx, name, LOOKUP_NAME_ISOLATED,
455			     NULL, NULL, &sid, &type);
456	TALLOC_FREE(mem_ctx);
457
458	if (exists) {
459		return NT_STATUS_ALIAS_EXISTS;
460	}
461
462	if (!winbind_allocate_gid(&gid)) {
463		DEBUG(3, ("Could not get a gid out of winbind\n"));
464		return NT_STATUS_ACCESS_DENIED;
465	}
466
467	if (!pdb_new_rid(&new_rid)) {
468		DEBUG(0, ("Could not allocate a RID -- wasted a gid :-(\n"));
469		return NT_STATUS_ACCESS_DENIED;
470	}
471
472	DEBUG(10, ("Creating alias %s with gid %d and rid %d\n",
473		   name, gid, new_rid));
474
475	sid_copy(&sid, get_global_sam_sid());
476	sid_append_rid(&sid, new_rid);
477
478	map.gid = gid;
479	sid_copy(&map.sid, &sid);
480	map.sid_name_use = SID_NAME_ALIAS;
481	fstrcpy(map.nt_name, name);
482	fstrcpy(map.comment, "");
483
484	status = pdb_add_group_mapping_entry(&map);
485
486	if (!NT_STATUS_IS_OK(status)) {
487		DEBUG(0, ("Could not add group mapping entry for alias %s "
488			  "(%s)\n", name, nt_errstr(status)));
489		return status;
490	}
491
492	*rid = new_rid;
493
494	return NT_STATUS_OK;
495}
496
497NTSTATUS pdb_default_delete_alias(struct pdb_methods *methods,
498				  const DOM_SID *sid)
499{
500	return pdb_delete_group_mapping_entry(*sid);
501}
502
503NTSTATUS pdb_default_get_aliasinfo(struct pdb_methods *methods,
504				   const DOM_SID *sid,
505				   struct acct_info *info)
506{
507	GROUP_MAP map;
508
509	if (!pdb_getgrsid(&map, *sid))
510		return NT_STATUS_NO_SUCH_ALIAS;
511
512	if ((map.sid_name_use != SID_NAME_ALIAS) &&
513	    (map.sid_name_use != SID_NAME_WKN_GRP)) {
514		DEBUG(2, ("%s is a %s, expected an alias\n",
515			  sid_string_static(sid),
516			  sid_type_lookup(map.sid_name_use)));
517		return NT_STATUS_NO_SUCH_ALIAS;
518	}
519
520	fstrcpy(info->acct_name, map.nt_name);
521	fstrcpy(info->acct_desc, map.comment);
522	sid_peek_rid(&map.sid, &info->rid);
523	return NT_STATUS_OK;
524}
525
526NTSTATUS pdb_default_set_aliasinfo(struct pdb_methods *methods,
527				   const DOM_SID *sid,
528				   struct acct_info *info)
529{
530	GROUP_MAP map;
531
532	if (!pdb_getgrsid(&map, *sid))
533		return NT_STATUS_NO_SUCH_ALIAS;
534
535	fstrcpy(map.nt_name, info->acct_name);
536	fstrcpy(map.comment, info->acct_desc);
537
538	return pdb_update_group_mapping_entry(&map);
539}
540
541NTSTATUS pdb_default_add_aliasmem(struct pdb_methods *methods,
542				  const DOM_SID *alias, const DOM_SID *member)
543{
544	return add_aliasmem(alias, member);
545}
546
547NTSTATUS pdb_default_del_aliasmem(struct pdb_methods *methods,
548				  const DOM_SID *alias, const DOM_SID *member)
549{
550	return del_aliasmem(alias, member);
551}
552
553NTSTATUS pdb_default_enum_aliasmem(struct pdb_methods *methods,
554				   const DOM_SID *alias, DOM_SID **pp_members,
555				   size_t *p_num_members)
556{
557	return enum_aliasmem(alias, pp_members, p_num_members);
558}
559
560NTSTATUS pdb_default_alias_memberships(struct pdb_methods *methods,
561				       TALLOC_CTX *mem_ctx,
562				       const DOM_SID *domain_sid,
563				       const DOM_SID *members,
564				       size_t num_members,
565				       uint32 **pp_alias_rids,
566				       size_t *p_num_alias_rids)
567{
568	DOM_SID *alias_sids;
569	size_t i, num_alias_sids;
570	NTSTATUS result;
571
572	alias_sids = NULL;
573	num_alias_sids = 0;
574
575	result = alias_memberships(members, num_members,
576				   &alias_sids, &num_alias_sids);
577
578	if (!NT_STATUS_IS_OK(result))
579		return result;
580
581	*p_num_alias_rids = 0;
582
583	if (num_alias_sids == 0) {
584		TALLOC_FREE(alias_sids);
585		return NT_STATUS_OK;
586	}
587
588	*pp_alias_rids = TALLOC_ARRAY(mem_ctx, uint32, num_alias_sids);
589	if (*pp_alias_rids == NULL)
590		return NT_STATUS_NO_MEMORY;
591
592	for (i=0; i<num_alias_sids; i++) {
593		if (!sid_peek_check_rid(domain_sid, &alias_sids[i],
594					&(*pp_alias_rids)[*p_num_alias_rids]))
595			continue;
596		*p_num_alias_rids += 1;
597	}
598
599	TALLOC_FREE(alias_sids);
600
601	return NT_STATUS_OK;
602}
603
604/**********************************************************************
605 no ops for passdb backends that don't implement group mapping
606 *********************************************************************/
607
608NTSTATUS pdb_nop_getgrsid(struct pdb_methods *methods, GROUP_MAP *map,
609				 DOM_SID sid)
610{
611	return NT_STATUS_UNSUCCESSFUL;
612}
613
614NTSTATUS pdb_nop_getgrgid(struct pdb_methods *methods, GROUP_MAP *map,
615				 gid_t gid)
616{
617	return NT_STATUS_UNSUCCESSFUL;
618}
619
620NTSTATUS pdb_nop_getgrnam(struct pdb_methods *methods, GROUP_MAP *map,
621				 const char *name)
622{
623	return NT_STATUS_UNSUCCESSFUL;
624}
625
626NTSTATUS pdb_nop_add_group_mapping_entry(struct pdb_methods *methods,
627						GROUP_MAP *map)
628{
629	return NT_STATUS_UNSUCCESSFUL;
630}
631
632NTSTATUS pdb_nop_update_group_mapping_entry(struct pdb_methods *methods,
633						   GROUP_MAP *map)
634{
635	return NT_STATUS_UNSUCCESSFUL;
636}
637
638NTSTATUS pdb_nop_delete_group_mapping_entry(struct pdb_methods *methods,
639						   DOM_SID sid)
640{
641	return NT_STATUS_UNSUCCESSFUL;
642}
643
644NTSTATUS pdb_nop_enum_group_mapping(struct pdb_methods *methods,
645					   enum lsa_SidType sid_name_use,
646					   GROUP_MAP **rmap, size_t *num_entries,
647					   BOOL unix_only)
648{
649	return NT_STATUS_UNSUCCESSFUL;
650}
651
652/****************************************************************************
653 These need to be redirected through pdb_interface.c
654****************************************************************************/
655BOOL pdb_get_dom_grp_info(const DOM_SID *sid, struct acct_info *info)
656{
657	GROUP_MAP map;
658	BOOL res;
659
660	become_root();
661	res = get_domain_group_from_sid(*sid, &map);
662	unbecome_root();
663
664	if (!res)
665		return False;
666
667	fstrcpy(info->acct_name, map.nt_name);
668	fstrcpy(info->acct_desc, map.comment);
669	sid_peek_rid(sid, &info->rid);
670	return True;
671}
672
673BOOL pdb_set_dom_grp_info(const DOM_SID *sid, const struct acct_info *info)
674{
675	GROUP_MAP map;
676
677	if (!get_domain_group_from_sid(*sid, &map))
678		return False;
679
680	fstrcpy(map.nt_name, info->acct_name);
681	fstrcpy(map.comment, info->acct_desc);
682
683	return NT_STATUS_IS_OK(pdb_update_group_mapping_entry(&map));
684}
685
686/********************************************************************
687 Really just intended to be called by smbd
688********************************************************************/
689
690NTSTATUS pdb_create_builtin_alias(uint32 rid)
691{
692	DOM_SID sid;
693	enum lsa_SidType type;
694	gid_t gid;
695	GROUP_MAP map;
696	TALLOC_CTX *mem_ctx;
697	NTSTATUS status;
698	const char *name = NULL;
699	fstring groupname;
700
701	DEBUG(10, ("Trying to create builtin alias %d\n", rid));
702
703	if ( !sid_compose( &sid, &global_sid_Builtin, rid ) ) {
704		return NT_STATUS_NO_SUCH_ALIAS;
705	}
706
707	if ( (mem_ctx = talloc_new(NULL)) == NULL ) {
708		return NT_STATUS_NO_MEMORY;
709	}
710
711	if ( !lookup_sid(mem_ctx, &sid, NULL, &name, &type) ) {
712		TALLOC_FREE( mem_ctx );
713		return NT_STATUS_NO_SUCH_ALIAS;
714	}
715
716	/* validate RID so copy the name and move on */
717
718	fstrcpy( groupname, name );
719	TALLOC_FREE( mem_ctx );
720
721	if (!winbind_allocate_gid(&gid)) {
722		DEBUG(3, ("pdb_create_builtin_alias: Could not get a gid out of winbind\n"));
723		return NT_STATUS_ACCESS_DENIED;
724	}
725
726	DEBUG(10,("Creating alias %s with gid %d\n", name, gid));
727
728	map.gid = gid;
729	sid_copy(&map.sid, &sid);
730	map.sid_name_use = SID_NAME_ALIAS;
731	fstrcpy(map.nt_name, name);
732	fstrcpy(map.comment, "");
733
734	status = pdb_add_group_mapping_entry(&map);
735
736	if (!NT_STATUS_IS_OK(status)) {
737		DEBUG(0, ("pdb_create_builtin_alias: Could not add group mapping entry for alias %d "
738			  "(%s)\n", rid, nt_errstr(status)));
739	}
740
741	return status;
742}
743
744
745