1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

Changes from APPLIANCE_HEAD (per Tim Potter):

- make proto
	- addition of function to convert from errno values to NT status codes
	  (source/lib/error.c)
	- purge queue done without full access permission will purge only the
	  jobs owned by that user, rather than failing.
	- unlock job database tdb before sending job to printer
	- in print_job_start(), ensure that we don't pick a jobid with an existing
	  temporary file that may be owned by another user, as it causes silent
	  failures.
	- fixes for printer permission checking for NT5 clients
	  (source/include/rpc_spoolss.h, source/printing/nt_printing.c,
	   source/printing/printing.c, source/rpc_server/srv_spoolss_nt.c)
	- change from uint8 to 'enum SID_NAME_USE' (source/rpc_server/srv_lsa.c)
	- fixed memory leaks for win95 driver download process
	  (source/smbd/lanman.c)
	- properly free prs_structs and dacl in testsuite/printing/psec.c
This commit is contained in:
David O'Neill 0001-01-01 00:00:00 +00:00
parent 8317d70a35
commit 74af3e2cae
10 changed files with 222 additions and 64 deletions

View File

@ -108,7 +108,7 @@ LIB_OBJ = lib/charcnv.o lib/charset.o lib/debug.o lib/fault.o \
lib/util_unistr.o lib/util_file.o \
lib/util.o lib/util_sock.o lib/util_sec.o smbd/ssl.o \
lib/talloc.o lib/hash.o lib/substitute.o lib/fsusage.o \
lib/ms_fnmatch.o lib/select.o \
lib/ms_fnmatch.o lib/select.o lib/error.o \
$(TDB_OBJ)
UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \

View File

@ -93,6 +93,10 @@ SMB_OFF_T dos_file_size(char *file_name);
int dos_ChDir(char *path);
char *dos_GetWd(char *path);
/*The following definitions come from lib/error.c */
uint32 map_nt_error_from_unix(int unix_error);
/*The following definitions come from lib/fault.c */
void fault_setup(void (*fn)(void *));
@ -1709,8 +1713,7 @@ BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level,
fstring value, uint8 **data, uint32 *type, uint32 *len);
uint32 nt_printing_setsec(char *printername, SEC_DESC_BUF *secdesc_ctr);
BOOL nt_printing_getsec(char *printername, SEC_DESC_BUF **secdesc_ctr);
BOOL print_access_check(struct current_user *user, int snum,
uint32 required_access);
BOOL print_access_check(struct current_user *user, int snum, int access_type);
BOOL print_time_access_check(int snum);
#endif

View File

@ -145,13 +145,18 @@
#define PRINTER_STATUS_POWER_SAVE 0x01000000
/* Printer permissions ACE settings */
/* Printer permissions ACE settings. NT4 uses generic and standard access
rights whereas NT5 converts them all to object specific access rights. */
#define PRINTER_ACE_FULL_CONTROL GENERIC_ALL_ACCESS
#define PRINTER_ACE_MANAGE_DOCUMENTS READ_CONTROL_ACCESS
#define PRINTER_ACE_PRINT \
(GENERIC_READ_ACCESS | GENERIC_WRITE_ACCESS | GENERIC_EXECUTE_ACCESS)
#define PRINTER_ACE_NT5_FULL_CONTROL 0x000f000c
#define PRINTER_ACE_NT5_PRINT 0x00020000
#define PRINTER_ACE_NT5_MANAGE_DOCUMENTS 0x00020008
#define SERVER_ACCESS_ADMINISTER 0x00000001
#define SERVER_ACCESS_ENUMERATE 0x00000002
#define PRINTER_ACCESS_ADMINISTER 0x00000004

75
source/lib/error.c Normal file
View File

@ -0,0 +1,75 @@
/*
* Unix SMB/Netbios implementation.
* Version 1.9
* Unix/DOS/NT error code conversions
* Copyright (C) Tim Potter 2000
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
/* Mapping between Unix, DOS and NT error numbers */
struct {
int unix_error;
int dos_error;
uint32 nt_error;
} unix_dos_nt_errmap[] = {
{ EPERM, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
{ EACCES, ERRnoaccess, NT_STATUS_ACCESS_DENIED },
{ ENOENT, ERRbadfile, NT_STATUS_NO_SUCH_FILE },
{ ENOTDIR, ERRbadpath, NT_STATUS_NOT_A_DIRECTORY },
{ EIO, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR },
{ EBADF, ERRsrverror, NT_STATUS_INVALID_HANDLE },
{ EINVAL, ERRsrverror, NT_STATUS_INVALID_HANDLE },
{ EEXIST, ERRfilexists, NT_STATUS_ACCESS_DENIED},
{ ENFILE, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
{ EMFILE, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES },
{ ENOSPC, ERRdiskfull, NT_STATUS_DISK_FULL },
#ifdef EDQUOT
{ EDQUOT, ERRdiskfull, NT_STATUS_DISK_FULL },
#endif
#ifdef ENOTEMPTY
{ ENOTEMPTY, ERRnoaccess, NT_STATUS_DIRECTORY_NOT_EMPTY },
#endif
#ifdef EXDEV
{ EXDEV, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE },
#endif
{ EROFS, ERRnowrite, NT_STATUS_ACCESS_DENIED },
{ 0, 0, 0 }
};
/* Map an NT error code from a Unix error code */
uint32 map_nt_error_from_unix(int unix_error)
{
int i = 0;
/* Look through list */
while(unix_dos_nt_errmap[i].unix_error != 0) {
if (unix_dos_nt_errmap[i].unix_error == unix_error) {
return unix_dos_nt_errmap[i].nt_error;
}
i++;
}
/* Default return */
return NT_STATUS_ACCESS_DENIED;
}

View File

@ -2203,30 +2203,47 @@ jfm: I should use this comment for the text file to explain
*/
/****************************************************************************
Check a user has permissions to perform the given operation
Check a user has permissions to perform the given operation. We use some
constants defined in include/rpc_spoolss.h that look relevant to check
the various actions we perform when checking printer access.
PRINTER_ACCESS_ADMINISTER:
print_queue_pause, print_queue_resume, update_printer_sec,
update_printer, spoolss_addprinterex_level_2,
_spoolss_setprinterdata
PRINTER_ACCESS_USE:
print_job_start
JOB_ACCESS_ADMINISTER:
print_job_delete, print_job_pause, print_job_resume,
print_queue_purge
if user is NULL then use the current_user structure
****************************************************************************/
BOOL print_access_check(struct current_user *user, int snum,
uint32 required_access)
BOOL print_access_check(struct current_user *user, int snum, int access_type)
{
SEC_DESC_BUF *secdesc = NULL;
uint32 access_granted, status;
uint32 access_granted, status, required_access = 0;
BOOL result;
char *pname;
int i;
extern struct current_user current_user;
/* If user is NULL then use the current_user structure */
if (!user) user = &current_user;
/* always allow root or printer admins to do anything */
if (user->uid==0 ||
/* Always allow root or printer admins to do anything */
if (user->uid == 0 ||
user_in_list(uidtoname(user->uid), lp_printer_admin(snum))) {
return True;
}
/* Get printer name */
pname = PRINTERNAME(snum);
if (!pname || !*pname)
pname = SERVICE(snum);
@ -2236,8 +2253,34 @@ BOOL print_access_check(struct current_user *user, int snum,
}
/* Get printer security descriptor */
nt_printing_getsec(pname, &secdesc);
/* Check against NT4 ACE mask values. From observation these
values are:
Access Type ACE Mask Constant
-------------------------------------
Full Control 0x10000000 PRINTER_ACE_FULL_CONTROL
Print 0xe0000000 PRINTER_ACE_PRINT
Manage Documents 0x00020000 PRINTER_ACE_MANAGE_DOCUMENTS
*/
switch (access_type) {
case PRINTER_ACCESS_USE:
required_access = PRINTER_ACE_PRINT;
break;
case PRINTER_ACCESS_ADMINISTER:
required_access = PRINTER_ACE_MANAGE_DOCUMENTS |
PRINTER_ACE_PRINT;
break;
case JOB_ACCESS_ADMINISTER:
required_access = PRINTER_ACE_MANAGE_DOCUMENTS;
default:
DEBUG(0, ("invalid value passed to print_access_check()\n"));
return False;
}
/* The ACE for Full Control in a printer security descriptor
doesn't seem to map properly to the access checking model. For
it to work properly it should be the logical OR of all the other
@ -2249,16 +2292,6 @@ BOOL print_access_check(struct current_user *user, int snum,
performing the access check. I'm sure there is a better way to
do this! */
/* You forgot to also change the *required access* from PRINTER_ACE_FULL_CONTROL
to PRINTER_ACE_MANAGE_DOCUMENTS | PRINTER_ACE_PRINT before doing the check.
This took me 3 hours to find !!!!! JRA.
*/
if (required_access & PRINTER_ACE_FULL_CONTROL) {
required_access |= (PRINTER_ACE_MANAGE_DOCUMENTS | PRINTER_ACE_PRINT);
required_access &= ~PRINTER_ACE_FULL_CONTROL;
}
if (secdesc && secdesc->sec && secdesc->sec->dacl &&
secdesc->sec->dacl->ace) {
for(i = 0; i < secdesc->sec->dacl->num_aces; i++) {
@ -2271,14 +2304,46 @@ BOOL print_access_check(struct current_user *user, int snum,
}
}
/* Check access */
if ((result = se_access_check(secdesc->sec, user, required_access,
&access_granted, &status))) {
goto done;
}
/* Check against NT5 ACE mask values. From observation these
values are:
Access Type ACE Mask Constant
-------------------------------------
Full Control 0x000f000c PRINTER_ACE_NT5_FULL_CONTROL
Print 0x00020008 PRINTER_ACE_NT5_PRINT
Manage Documents 0x00020000 PRINTER_ACE_NT5_MANAGE_DOCUMENTS
NT5 likes to rewrite the security descriptor and change the ACE
masks from NT4 format to NT5 format making them unreadable by
NT4 clients. */
switch (access_type) {
case PRINTER_ACCESS_USE:
required_access = PRINTER_ACE_NT5_PRINT;
break;
case PRINTER_ACCESS_ADMINISTER:
required_access = PRINTER_ACE_NT5_FULL_CONTROL;
break;
case JOB_ACCESS_ADMINISTER:
required_access = PRINTER_ACE_NT5_MANAGE_DOCUMENTS;
break;
}
result = se_access_check(secdesc->sec, user, required_access,
&access_granted, &status);
/* Check access */
done:
DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE"));
/* Free mallocated memory */
free_sec_desc_buf(&secdesc);
if (!result)

View File

@ -507,7 +507,7 @@ BOOL print_job_delete(struct current_user *user, int jobid)
owns their job. */
if (!owner &&
!print_access_check(user, snum, PRINTER_ACE_MANAGE_DOCUMENTS)) {
!print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
DEBUG(3, ("delete denied by security descriptor\n"));
return False;
}
@ -542,7 +542,7 @@ BOOL print_job_pause(struct current_user *user, int jobid)
owner = is_owner(user->uid, jobid);
if (!owner &&
!print_access_check(user, snum, PRINTER_ACE_MANAGE_DOCUMENTS)) {
!print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
DEBUG(3, ("pause denied by security descriptor\n"));
return False;
}
@ -579,7 +579,7 @@ BOOL print_job_resume(struct current_user *user, int jobid)
owner = is_owner(user->uid, jobid);
if (!is_owner(user->uid, jobid) &&
!print_access_check(user, snum, PRINTER_ACE_MANAGE_DOCUMENTS)) {
!print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
DEBUG(3, ("resume denied by security descriptor\n"));
return False;
}
@ -624,9 +624,9 @@ int print_job_start(struct current_user *user, int snum, char *jobname)
errno = 0;
if (!print_access_check(user, snum, PRINTER_ACE_PRINT)) {
if (!print_access_check(user, snum, PRINTER_ACCESS_USE)) {
DEBUG(3, ("job start denied by security descriptor\n"));
return False;
return -1;
}
path = lp_pathname(snum);
@ -665,6 +665,7 @@ int print_job_start(struct current_user *user, int snum, char *jobname)
/* lock the database */
tdb_writelock(tdb);
next_jobnum:
next_jobid = tdb_fetch_int(tdb, "INFO/nextjob");
if (next_jobid == -1) next_jobid = 1;
@ -684,17 +685,20 @@ int print_job_start(struct current_user *user, int snum, char *jobname)
we unlink first to cope with old spool files and also to beat
a symlink security hole - it allows us to use O_EXCL
There may be old spool files owned by other users lying around.
*/
slprintf(pjob.filename, sizeof(pjob.filename), "%s/%s%d",
path, PRINT_SPOOL_PREFIX, jobid);
if (unlink(pjob.filename) == -1 && errno != ENOENT) {
goto fail;
goto next_jobnum;
}
pjob.fd = sys_open(pjob.filename,O_WRONLY|O_CREAT|O_EXCL,0600);
if (pjob.fd == -1) goto fail;
print_job_store(jobid, &pjob);
tdb_writeunlock(tdb);
/*
* If the printer is marked as postscript output a leading
* file identifier to ensure the file is treated as a raw
@ -706,7 +710,6 @@ int print_job_start(struct current_user *user, int snum, char *jobname)
print_job_write(jobid, "%!\n",3);
}
tdb_writeunlock(tdb);
return jobid;
fail:
@ -896,7 +899,7 @@ BOOL print_queue_pause(struct current_user *user, int snum, int *errcode)
if (!user) return False;
if (!print_access_check(user, snum, PRINTER_ACE_MANAGE_DOCUMENTS)) {
if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
*errcode = ERROR_ACCESS_DENIED;
return False;
}
@ -917,7 +920,7 @@ BOOL print_queue_resume(struct current_user *user, int snum, int *errcode)
{
int ret;
if (!print_access_check(user, snum, PRINTER_ACE_MANAGE_DOCUMENTS)) {
if (!print_access_check(user, snum, PRINTER_ACCESS_ADMINISTER)) {
*errcode = ERROR_ACCESS_DENIED;
return False;
}
@ -940,15 +943,12 @@ BOOL print_queue_purge(struct current_user *user, int snum, int *errcode)
print_status_struct status;
int njobs, i;
if (!print_access_check(user, snum, PRINTER_ACE_MANAGE_DOCUMENTS)) {
*errcode = ERROR_ACCESS_DENIED;
return False;
}
njobs = print_queue_status(snum, &queue, &status);
for (i=0;i<njobs;i++) {
if (print_access_check(user, snum, JOB_ACCESS_ADMINISTER)) {
print_job_delete1(queue[i].job);
}
}
print_cache_flush(snum);

View File

@ -219,7 +219,7 @@ static void init_lsa_rid2s(DOM_R_REF *ref, DOM_RID2 *rid2,
pstring full_name;
fstring dom_name;
fstring user;
uint8 sid_name_use = SID_NAME_UNKNOWN;
enum SID_NAME_USE sid_name_use = SID_NAME_UNKNOWN;
pstrcpy(full_name, dos_unistr2_to_str(&name[i]));
@ -298,7 +298,7 @@ static void init_lsa_trans_names(DOM_R_REF *ref, LSA_TRANS_NAME_ENUM *trn,
uint32 rid = 0xffffffff;
int dom_idx = -1;
fstring name, dom_name;
uint8 sid_name_use = 0;
enum SID_NAME_USE sid_name_use = 0;
/* Lookup sid from winbindd */

View File

@ -22,7 +22,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
extern int DEBUGLEVEL;
@ -2936,10 +2935,11 @@ uint32 _spoolss_startdocprinter(POLICY_HND *handle, uint32 level,
Printer->jobid = print_job_start(&user, snum, jobname);
/* need to map error codes properly - for now give out of
memory as I don't know the correct codes (tridge) */
/* An error occured in print_job_start() so return an appropriate
NT error code. */
if (Printer->jobid == -1) {
return ERROR_NOT_ENOUGH_MEMORY;
return map_nt_error_from_unix(errno);
}
Printer->document_started=True;
@ -3082,7 +3082,7 @@ static uint32 update_printer_sec(POLICY_HND *handle, uint32 level,
descriptor. By experimentation with two NT machines, the user
requires Full Access to the printer to change security
information. */
if (!print_access_check(&user, snum, PRINTER_ACE_FULL_CONTROL)) {
if (!print_access_check(&user, snum, PRINTER_ACCESS_ADMINISTER)) {
result = ERROR_ACCESS_DENIED;
goto done;
}
@ -3172,13 +3172,13 @@ static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer)
numlines = 0;
qlines = file_lines_load(tmp_file, &numlines);
DEBUGADD(10,("Lines returned = [%d]\n", numlines));
DEBUGADD(10,("Line[0] = [%s]\n", qlines[0]));
DEBUGADD(10,("Unlinking port file [%s]\n", tmp_file));
unlink(tmp_file);
if(numlines) {
// Set the portname to what the script says the portname should be
strncpy(printer->info_2->portname, qlines[0], sizeof(printer->info_2->portname));
DEBUGADD(6,("Line[0] = [%s]\n", qlines[0]));
// Send SIGHUP to process group... is there a better way?
kill(0, SIGHUP);
@ -3226,7 +3226,7 @@ static uint32 update_printer(POLICY_HND *handle, uint32 level,
goto done;
}
if (!print_access_check(NULL, snum, PRINTER_ACE_FULL_CONTROL)) {
if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
DEBUG(3, ("printer property change denied by security "
"descriptor\n"));
result = ERROR_ACCESS_DENIED;
@ -4028,7 +4028,6 @@ static uint32 enumports_level_1(NEW_BUFFER *buffer, uint32 offered, uint32 *need
numlines = 0;
qlines = file_lines_load(tmp_file, &numlines);
DEBUGADD(10,("Lines returned = [%d]\n", numlines));
DEBUGADD(10,("Line[0] = [%s]\n", qlines[0]));
DEBUGADD(10,("Unlinking port file [%s]\n", tmp_file));
unlink(tmp_file);
@ -4127,7 +4126,6 @@ static uint32 enumports_level_2(NEW_BUFFER *buffer, uint32 offered, uint32 *need
numlines = 0;
qlines = file_lines_load(tmp_file, &numlines);
DEBUGADD(10,("Lines returned = [%d]\n", numlines));
DEBUGADD(10,("Line[0] = [%s]\n", qlines[0]));
DEBUGADD(10,("Unlinking port file [%s]\n", tmp_file));
unlink(tmp_file);
@ -4247,7 +4245,7 @@ static uint32 spoolss_addprinterex_level_2( const UNISTR2 *uni_srv_name,
}
/* you must be a printer admin to add a new printer */
if (!print_access_check(NULL, snum, PRINTER_ACE_FULL_CONTROL)) {
if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
free_a_printer(&printer,2);
return ERROR_ACCESS_DENIED;
}
@ -4564,7 +4562,7 @@ uint32 _spoolss_setprinterdata( POLICY_HND *handle,
if (!get_printer_snum(handle, &snum))
return ERROR_INVALID_HANDLE;
if (!print_access_check(NULL, snum, PRINTER_ACE_FULL_CONTROL)) {
if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
DEBUG(3, ("security descriptor change denied by existing "
"security descriptor\n"));
return ERROR_ACCESS_DENIED;

View File

@ -540,7 +540,7 @@ static void fill_printq_info_52(connection_struct *conn, int snum, int uLevel,
DEBUG(3,("Can't open %s - %s\n", lp_driverfile(snum),
strerror(errno)));
desc->errcode=NERR_notsupported;
return;
goto done;
}
/* lookup the long printer driver name in the file description */
@ -651,14 +651,16 @@ static void fill_printq_info_52(connection_struct *conn, int snum, int uLevel,
SERVICE(snum),count));
desc->errcode=NERR_Success;
file_lines_free(lines);
return;
goto done;
}
err:
DEBUG(3,("fill_printq_info: Can't supply driver files\n"));
desc->errcode=NERR_notsupported;
done:
safe_free(info);
file_lines_free(lines);
}
@ -741,7 +743,7 @@ static void fill_printq_info(connection_struct *conn, int snum, int uLevel,
/* This function returns the number of files for a given driver */
static int get_printerdrivernumber(int snum)
{
int i;
int i, result = 0;
BOOL ok = False;
pstring tok;
char *p;
@ -777,7 +779,7 @@ static int get_printerdrivernumber(int snum)
if (!lines)
{
DEBUG(3,("Can't open %s - %s\n", lp_driverfile(snum),strerror(errno)));
return 0;
goto done;
}
/* lookup the long printer driver name in the file description */
@ -800,22 +802,24 @@ static int get_printerdrivernumber(int snum)
while (*p && i) {
if (*p++ == ':') i--;
}
if (!*p || i)
goto err;
if (!*p || i) {
DEBUG(3,("Can't determine number of printer driver files\n"));
goto done;
}
/* count the number of files */
while (next_token(&p,tok,",",sizeof(tok)))
i++;
file_lines_free(lines);
return(i);
result = i;
}
err:
done:
DEBUG(3,("Can't determine number of printer driver files\n"));
safe_free(info);
file_lines_free(lines);
return (0);
return result;
}
static BOOL api_DosPrintQGetInfo(connection_struct *conn,

View File

@ -165,6 +165,8 @@ int psec_getsec(char *printer)
prs_struct ps;
int result = 0, i;
ZERO_STRUCT(ps);
/* Open tdb for reading */
slprintf(tdb_path, sizeof(tdb_path) - 1, "%s/ntdrivers.tdb", LOCKDIR);
@ -242,6 +244,7 @@ int psec_getsec(char *printer)
if (tdb) tdb_close(tdb);
if (mem_ctx) talloc_destroy(mem_ctx);
if (secdesc_ctr) free_sec_desc_buf(&secdesc_ctr);
prs_mem_free(&ps);
return result;
}
@ -252,7 +255,7 @@ int psec_setsec(char *printer)
{
DOM_SID user_sid, group_sid;
SEC_ACE *ace_list = NULL;
SEC_ACL *dacl;
SEC_ACL *dacl = NULL;
SEC_DESC *sd;
SEC_DESC_BUF *sdb = NULL;
int result = 0, num_aces = 0;
@ -262,6 +265,8 @@ int psec_setsec(char *printer)
TALLOC_CTX *mem_ctx = NULL;
BOOL has_user_sid = False, has_group_sid = False;
ZERO_STRUCT(ps);
/* Open tdb for reading */
slprintf(tdb_path, sizeof(tdb_path) - 1, "%s/ntdrivers.tdb", LOCKDIR);
@ -327,6 +332,8 @@ int psec_setsec(char *printer)
dacl, /* Discretionary ACL */
&size);
free_sec_acl(&dacl);
sdb = make_sec_desc_buf(size, sd);
free_sec_desc(&sd);
@ -360,6 +367,7 @@ int psec_setsec(char *printer)
if (tdb) tdb_close(tdb);
if (sdb) free_sec_desc_buf(&sdb);
if (mem_ctx) talloc_destroy(mem_ctx);
prs_mem_free(&ps);
return result;
}