From a3a28675fafbbc5a5a378b3a7235253d772ef63e Mon Sep 17 00:00:00 2001 From: David O'Neill Date: Fri, 1 Sep 2000 18:49:26 +0000 Subject: 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 used to be commit 74af3e2caec7197e5d1ca389e2f78054a4197502) --- source3/Makefile.in | 2 +- source3/include/proto.h | 7 ++- source3/include/rpc_spoolss.h | 7 ++- source3/lib/error.c | 75 ++++++++++++++++++++++++++ source3/printing/nt_printing.c | 103 +++++++++++++++++++++++++++++------- source3/printing/printing.c | 30 +++++------ source3/rpc_server/srv_lsa.c | 4 +- source3/rpc_server/srv_spoolss_nt.c | 20 ++++--- source3/smbd/lanman.c | 28 +++++----- 9 files changed, 213 insertions(+), 63 deletions(-) create mode 100644 source3/lib/error.c (limited to 'source3') diff --git a/source3/Makefile.in b/source3/Makefile.in index 4e9475ea94..018086b40b 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -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 \ diff --git a/source3/include/proto.h b/source3/include/proto.h index 38393e3de8..9d239d38fd 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -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 diff --git a/source3/include/rpc_spoolss.h b/source3/include/rpc_spoolss.h index a77ce55063..eb521a5b94 100755 --- a/source3/include/rpc_spoolss.h +++ b/source3/include/rpc_spoolss.h @@ -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 diff --git a/source3/lib/error.c b/source3/lib/error.c new file mode 100644 index 0000000000..880eba5949 --- /dev/null +++ b/source3/lib/error.c @@ -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; +} diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c index 05ab71d178..0ad50a2277 100644 --- a/source3/printing/nt_printing.c +++ b/source3/printing/nt_printing.c @@ -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 = ¤t_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) diff --git a/source3/printing/printing.c b/source3/printing/printing.c index cf3748ed16..406cbf2c80 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -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,14 +943,11 @@ 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;ijobid = 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; diff --git a/source3/smbd/lanman.c b/source3/smbd/lanman.c index 6595163ba9..54bfa3155b 100644 --- a/source3/smbd/lanman.c +++ b/source3/smbd/lanman.c @@ -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, -- cgit