1
0
mirror of https://github.com/samba-team/samba.git synced 2025-07-31 20:22:15 +03:00

Merge of printer security descriptor, info level and printerdata

comparison changes from appliance branch.
This commit is contained in:
Tim Potter
-
parent 9a5471b3e8
commit ae087bdf31
3 changed files with 426 additions and 95 deletions

View File

@ -2174,73 +2174,20 @@ BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level,
uint32 nt_printing_setsec(char *printername, SEC_DESC_BUF *secdesc_ctr)
{
SEC_DESC_BUF *new_secdesc_ctr = NULL;
SEC_DESC_BUF *old_secdesc_ctr = NULL;
DOM_SID *owner_sid, *group_sid;
SEC_ACL *dacl, *sacl;
SEC_DESC *psd = NULL;
size_t secdesc_size;
prs_struct ps;
TALLOC_CTX *mem_ctx = NULL;
fstring key;
uint16 secdesc_type;
uint32 status;
mem_ctx = talloc_init();
if (mem_ctx == NULL) return False;
/* Make a copy of the security descriptor so we can fill in bits
that NT doesn't pass to us. Any pieces of the security descriptor
that are NULL are not meant to change. */
nt_printing_getsec(printername, &old_secdesc_ctr);
secdesc_type = secdesc_ctr->sec->type;
/* Copy over owner and group sids. There seems to be no flag for
this so just check the pointer values. */
owner_sid = secdesc_ctr->sec->owner_sid ?
secdesc_ctr->sec->owner_sid :
old_secdesc_ctr->sec->owner_sid;
group_sid = secdesc_ctr->sec->grp_sid ?
secdesc_ctr->sec->grp_sid :
old_secdesc_ctr->sec->grp_sid;
/* Ignore changes to the system ACL. This has the effect of making
changes through the security tab audit button not sticking.
Perhaps in future Samba could implement these settings somehow. */
sacl = NULL;
secdesc_type &= ~SEC_DESC_SACL_PRESENT;
/* Copy across discretionary ACL */
if (secdesc_type & SEC_DESC_DACL_PRESENT) {
dacl = secdesc_ctr->sec->dacl;
} else {
dacl = old_secdesc_ctr->sec->dacl;
secdesc_type |= SEC_DESC_DACL_PRESENT;
}
/* Create new security descriptor from bits */
psd = make_sec_desc(secdesc_ctr->sec->revision, secdesc_type,
owner_sid, group_sid, sacl, dacl, &secdesc_size);
new_secdesc_ctr = make_sec_desc_buf(secdesc_size, psd);
free_sec_desc(&psd);
free_sec_desc_buf(&old_secdesc_ctr);
/* Store the security descriptor in a tdb */
prs_init(&ps, (uint32)sec_desc_size(new_secdesc_ctr->sec) +
prs_init(&ps, (uint32)sec_desc_size(secdesc_ctr->sec) +
sizeof(SEC_DESC_BUF), 4, mem_ctx, MARSHALL);
if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr,
&ps, 1)) {
if (!sec_io_desc_buf("nt_printing_setsec", &secdesc_ctr, &ps, 1)) {
status = ERROR_INVALID_FUNCTION;
goto done;
}
@ -2257,9 +2204,6 @@ uint32 nt_printing_setsec(char *printername, SEC_DESC_BUF *secdesc_ctr)
/* Free mallocated memory */
done:
free_sec_desc_buf(&old_secdesc_ctr);
free_sec_desc_buf(&new_secdesc_ctr);
prs_mem_free(&ps);
if (mem_ctx) talloc_destroy(mem_ctx);

View File

@ -277,6 +277,163 @@ size_t sec_desc_size(SEC_DESC *psd)
return offset;
}
/*******************************************************************
Compares two SEC_ACE structures
********************************************************************/
BOOL sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
{
/* Trivial case */
if (!s1 && !s2) return True;
/* Check top level stuff */
if (s1->type != s2->type || s1->flags != s2->flags ||
s1->info.mask != s2->info.mask) {
return False;
}
/* Check SID */
if (!sid_equal(&s1->sid, &s2->sid)) {
return False;
}
return True;
}
/*******************************************************************
Compares two SEC_ACL structures
********************************************************************/
BOOL sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
{
int i, j;
/* Trivial case */
if (!s1 && !s2) return True;
/* Check top level stuff */
if (s1->revision != s2->revision || s1->num_aces != s2->num_aces) {
return False;
}
/* The ACEs could be in any order so check each ACE in s1 against
each ACE in s2. */
for (i = 0; i < s1->num_aces; i++) {
BOOL found = False;
for (j = 0; j < s2->num_aces; j++) {
if (sec_ace_equal(&s1->ace[i], &s2->ace[j])) {
found = True;
break;
}
}
if (!found) return False;
}
return True;
}
/*******************************************************************
Compares two SEC_DESC structures
********************************************************************/
BOOL sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
{
/* Trivial case */
if (!s1 && !s2) return True;
/* Check top level stuff */
if (s1->revision != s2->revision || s1->type != s2->type) {
return False;
}
/* Check owner and group */
if (!sid_equal(s1->owner_sid, s2->owner_sid) ||
!sid_equal(s1->grp_sid, s2->grp_sid)) {
return False;
}
/* Check ACLs present in one but not the other */
if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
(s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) {
return False;
}
/* Sigh - we have to do it the hard way by iterating over all
the ACEs in the ACLs */
if (!sec_acl_equal(s1->dacl, s2->dacl) ||
!sec_acl_equal(s1->sacl, s2->sacl)) {
return False;
}
return True;
}
/*******************************************************************
Merge part of security descriptor old_sec in to the empty sections of
security descriptor new_sec.
********************************************************************/
SEC_DESC_BUF *sec_desc_merge(SEC_DESC_BUF *new_sdb, SEC_DESC_BUF *old_sdb)
{
DOM_SID *owner_sid, *group_sid;
SEC_DESC_BUF *return_sdb;
SEC_ACL *dacl, *sacl;
SEC_DESC *psd = NULL;
uint16 secdesc_type;
size_t secdesc_size;
/* Copy over owner and group sids. There seems to be no flag for
this so just check the pointer values. */
owner_sid = new_sdb->sec->owner_sid ? new_sdb->sec->owner_sid :
old_sdb->sec->owner_sid;
group_sid = new_sdb->sec->grp_sid ? new_sdb->sec->grp_sid :
old_sdb->sec->grp_sid;
secdesc_type = new_sdb->sec->type;
/* Ignore changes to the system ACL. This has the effect of making
changes through the security tab audit button not sticking.
Perhaps in future Samba could implement these settings somehow. */
sacl = NULL;
secdesc_type &= ~SEC_DESC_SACL_PRESENT;
/* Copy across discretionary ACL */
if (secdesc_type & SEC_DESC_DACL_PRESENT) {
dacl = new_sdb->sec->dacl;
} else {
dacl = old_sdb->sec->dacl;
secdesc_type |= SEC_DESC_DACL_PRESENT;
}
/* Create new security descriptor from bits */
psd = make_sec_desc(new_sdb->sec->revision, secdesc_type,
owner_sid, group_sid, sacl, dacl, &secdesc_size);
return_sdb = make_sec_desc_buf(secdesc_size, psd);
free_sec_desc(&psd);
return(return_sdb);
}
/*******************************************************************
Creates a SEC_DESC structure
********************************************************************/

View File

@ -3325,7 +3325,7 @@ static uint32 control_printer(POLICY_HND *handle, uint32 command,
{
struct current_user user;
int snum;
int errcode = 0;
int errcode = ERROR_INVALID_FUNCTION;
Printer_entry *Printer = find_printer_index_by_hnd(handle);
get_current_user(&user, p);
@ -3335,35 +3335,29 @@ static uint32 control_printer(POLICY_HND *handle, uint32 command,
return ERROR_INVALID_HANDLE;
}
if (!get_printer_snum(handle, &snum) )
if (!get_printer_snum(handle, &snum))
return ERROR_INVALID_HANDLE;
switch (command) {
case PRINTER_CONTROL_PAUSE:
if (print_queue_pause(&user, snum, &errcode)) {
srv_spoolss_sendnotify(handle);
return 0;
errcode = 0;
}
break;
case PRINTER_CONTROL_RESUME:
case PRINTER_CONTROL_UNPAUSE:
if (print_queue_resume(&user, snum, &errcode)) {
srv_spoolss_sendnotify(handle);
return 0;
errcode = 0;
}
break;
case PRINTER_CONTROL_PURGE:
if (print_queue_purge(&user, snum, &errcode)) {
srv_spoolss_sendnotify(handle);
return 0;
errcode = 0;
}
break;
}
if (errcode)
return (uint32)errcode;
return ERROR_INVALID_FUNCTION;
return errcode;
}
/********************************************************************
@ -3383,6 +3377,7 @@ static uint32 update_printer_sec(POLICY_HND *handle, uint32 level,
const SPOOL_PRINTER_INFO_LEVEL *info,
pipes_struct *p, SEC_DESC_BUF *secdesc_ctr)
{
SEC_DESC_BUF *new_secdesc_ctr = NULL, *old_secdesc_ctr = NULL;
struct current_user user;
uint32 result;
int snum;
@ -3390,25 +3385,47 @@ static uint32 update_printer_sec(POLICY_HND *handle, uint32 level,
Printer_entry *Printer = find_printer_index_by_hnd(handle);
if (!OPEN_HANDLE(Printer) || !get_printer_snum(handle, &snum)) {
DEBUG(0,("update_printer_sec: Invalid handle (%s)\n", OUR_HANDLE(handle)));
return ERROR_INVALID_HANDLE;
DEBUG(0,("update_printer_sec: Invalid handle (%s)\n",
OUR_HANDLE(handle)));
result = ERROR_INVALID_HANDLE;
goto done;
}
/* NT seems to like setting the security descriptor even though
nothing may have actually changed. This causes annoying
dialog boxes when the user doesn't have permission to change
the security descriptor. */
nt_printing_getsec(Printer->dev.handlename, &old_secdesc_ctr);
new_secdesc_ctr = sec_desc_merge(secdesc_ctr, old_secdesc_ctr);
if (sec_desc_equal(new_secdesc_ctr->sec, old_secdesc_ctr->sec)) {
result = NT_STATUS_NO_PROBLEMO;
goto done;
}
/* Work out which user is performing the operation */
get_current_user(&user, p);
/* Check the user has permissions to change the security
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_ACCESS_ADMINISTER)) {
result = ERROR_ACCESS_DENIED;
goto done;
}
result = nt_printing_setsec(Printer->dev.handlename, secdesc_ctr);
result = nt_printing_setsec(Printer->dev.handlename, new_secdesc_ctr);
done:
free_sec_desc_buf(&new_secdesc_ctr);
free_sec_desc_buf(&old_secdesc_ctr);
return result;
}
@ -3522,6 +3539,181 @@ static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer)
return True;
}
/* Return true if two devicemodes are equal */
static BOOL nt_devicemode_equal(NT_DEVICEMODE *d1, NT_DEVICEMODE *d2)
{
if (!strequal(d1->devicename, d2->devicename) ||
!strequal(d1->formname, d2->formname)) {
return False;
}
if (d1->specversion != d2->specversion ||
d1->driverversion != d2->driverversion ||
d1->size != d2->size ||
d1->driverextra != d2->driverextra ||
d1->orientation != d2->orientation ||
d1->papersize != d2->papersize ||
d1->paperlength != d2->paperlength ||
d1->paperwidth != d2->paperwidth ||
d1->scale != d2->scale ||
d1->copies != d2->copies ||
d1->defaultsource != d2->defaultsource ||
d1->printquality != d2->printquality ||
d1->color != d2->color ||
d1->duplex != d2->duplex ||
d1->yresolution != d2->yresolution ||
d1->ttoption != d2->ttoption ||
d1->collate != d2->collate ||
d1->logpixels != d2->logpixels) {
return False;
}
if (d1->fields != d2->fields ||
d1->bitsperpel != d2->bitsperpel ||
d1->pelswidth != d2->pelswidth ||
d1->pelsheight != d2->pelsheight ||
d1->displayflags != d2->displayflags ||
d1->displayfrequency != d2->displayfrequency ||
d1->icmmethod != d2->icmmethod ||
d1->icmintent != d2->icmintent ||
d1->mediatype != d2->mediatype ||
d1->dithertype != d2->dithertype ||
d1->reserved1 != d2->reserved1 ||
d1->reserved2 != d2->reserved2 ||
d1->panningwidth != d2->panningwidth ||
d1->panningheight != d2->panningheight) {
return False;
}
/* Not sure what to do about these fields */
#if 0
uint8 *private;
#endif
return True;
}
/* Return true if two NT_PRINTER_PARAM structures are equal */
static BOOL nt_printer_param_equal(NT_PRINTER_PARAM *p1,
NT_PRINTER_PARAM *p2)
{
if (!p1 && !p2) return True;
if ((!p1 && p2) || (p1 && !p2)) return False;
/* Compare lists of printer parameters */
while (p1) {
BOOL found = False;
NT_PRINTER_PARAM *q = p1;
/* Find the parameter in the second structure */
while(q) {
if (strequal(p1->value, q->value) &&
p1->type == q->type &&
p1->data_len == q->data_len &&
memcmp(p1->data, q->data, p1->data_len) == 0) {
found = True;
goto found_it;
}
q = q->next;
}
found_it:
if (!found) {
return False;
}
p1 = p1->next;
}
return True;
}
/********************************************************************
* Called by update_printer when trying to work out whether to
* actually update printer info.
********************************************************************/
static BOOL nt_printer_info_level_equal(NT_PRINTER_INFO_LEVEL *p1,
NT_PRINTER_INFO_LEVEL *p2)
{
NT_PRINTER_INFO_LEVEL_2 *pi1, *pi2;
/* Trivial conditions */
if ((!p1 && !p2) || (!p1->info_2 && !p2->info_2)) {
return True;
}
if ((!p1 && p2) || (p1 && !p2) ||
(!p1->info_2 && p2->info_2) ||
(p1->info_2 && !p2->info_2)) {
return False;
}
/* Compare two nt_printer_info_level structures. Don't compare
status or cjobs as they seem to have something to do with the
printer queue. */
pi1 = p1->info_2;
pi2 = p2->info_2;
if (pi1->attributes != pi2->attributes ||
pi1->priority != pi2->priority ||
pi1->default_priority != pi2->default_priority ||
pi1->starttime != pi2->starttime ||
pi1->untiltime != pi2->untiltime ||
pi1->averageppm != pi2->averageppm) {
return False;
}
/* Yuck - don't check the printername or servername as the
add_a_printer() code plays games with them. You can't
change the printername or the sharename through this interface
in Samba. */
if (!strequal(pi1->sharename, pi2->sharename) ||
!strequal(pi1->portname, pi2->portname) ||
!strequal(pi1->drivername, pi2->drivername) ||
!strequal(pi1->comment, pi2->comment) ||
!strequal(pi1->location, pi2->location)) {
return False;
}
if (!nt_devicemode_equal(pi1->devmode, pi2->devmode)) {
return False;
}
if (!strequal(pi1->sepfile, pi2->sepfile) ||
!strequal(pi1->printprocessor, pi2->printprocessor) ||
!strequal(pi1->datatype, pi2->datatype) ||
!strequal(pi1->parameters, pi2->parameters)) {
return False;
}
if (!nt_printer_param_equal(pi1->specific, pi2->specific)) {
return False;
}
if (!sec_desc_equal(pi1->secdesc_buf->sec, pi2->secdesc_buf->sec)) {
return False;
}
if (pi1->changeid != pi2->changeid ||
pi1->c_setprinter != pi2->c_setprinter ||
pi1->setuptime != pi2->setuptime) {
return False;
}
return True;
}
/********************************************************************
* called by spoolss_api_setprinter
* when updating a printer description
@ -3532,7 +3724,7 @@ static uint32 update_printer(POLICY_HND *handle, uint32 level,
DEVICEMODE *devmode)
{
int snum;
NT_PRINTER_INFO_LEVEL *printer = NULL;
NT_PRINTER_INFO_LEVEL *printer = NULL, *old_printer = NULL;
Printer_entry *Printer = find_printer_index_by_hnd(handle);
uint32 result;
@ -3540,8 +3732,6 @@ static uint32 update_printer(POLICY_HND *handle, uint32 level,
result = NT_STATUS_NO_PROBLEMO;
/* Check calling user has permission to update printer description */
if (level!=2) {
DEBUG(0,("Send a mail to samba@samba.org\n"));
DEBUGADD(0,("with the following message: update_printer: level!=2\n"));
@ -3559,14 +3749,8 @@ static uint32 update_printer(POLICY_HND *handle, uint32 level,
goto done;
}
if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
DEBUG(3, ("printer property change denied by security "
"descriptor\n"));
result = ERROR_ACCESS_DENIED;
goto done;
}
if(get_a_printer(&printer, 2, lp_servicename(snum)) != 0) {
if((get_a_printer(&printer, 2, lp_servicename(snum)) != 0) ||
(get_a_printer(&old_printer, 2, lp_servicename(snum)) != 0)) {
result = ERROR_INVALID_HANDLE;
goto done;
}
@ -3602,21 +3786,42 @@ static uint32 update_printer(POLICY_HND *handle, uint32 level,
printer->info_2->devmode=NULL;
}
/*
* Do sanity check on the requested changes for Samba.
*/
/* Do sanity check on the requested changes for Samba */
if (!check_printer_ok(printer->info_2, snum)) {
result = ERROR_INVALID_PARAMETER;
goto done;
}
/* NT likes to call this function even though nothing has actually
changed. Check this so the user doesn't end up with an
annoying permission denied dialog box. */
if (nt_printer_info_level_equal(printer, old_printer)) {
DEBUG(3, ("printer info has not changed\n"));
result = NT_STATUS_NO_PROBLEMO;
goto done;
}
/* Check calling user has permission to update printer description */
if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
DEBUG(3, ("printer property change denied by security "
"descriptor\n"));
result = ERROR_ACCESS_DENIED;
goto done;
}
/* Call addprinter hook */
if (*lp_addprinter_cmd() )
if ( !add_printer_hook(printer) ) {
result = ERROR_ACCESS_DENIED;
goto done;
}
/* Update printer info */
if (add_a_printer(*printer, 2)!=0) {
/* I don't really know what to return here !!! */
result = ERROR_ACCESS_DENIED;
@ -4988,14 +5193,13 @@ uint32 _spoolss_setprinterdata( POLICY_HND *handle,
uint32 numeric_data)
{
NT_PRINTER_INFO_LEVEL *printer = NULL;
NT_PRINTER_PARAM *param = NULL;
NT_PRINTER_PARAM *param = NULL, old_param;
int snum=0;
uint32 status = 0x0;
Printer_entry *Printer=find_printer_index_by_hnd(handle);
DEBUG(5,("spoolss_setprinterdata\n"));
if (!OPEN_HANDLE(Printer)) {
DEBUG(0,("_spoolss_setprinterdata: Invalid handle (%s).\n", OUR_HANDLE(handle)));
return ERROR_INVALID_HANDLE;
@ -5004,17 +5208,40 @@ uint32 _spoolss_setprinterdata( POLICY_HND *handle,
if (!get_printer_snum(handle, &snum))
return ERROR_INVALID_HANDLE;
if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
DEBUG(3, ("security descriptor change denied by existing "
"security descriptor\n"));
return ERROR_ACCESS_DENIED;
}
status = get_a_printer(&printer, 2, lp_servicename(snum));
if (status != 0x0)
return ERROR_INVALID_NAME;
convert_specific_param(&param, value , type, data, real_len);
/* Check if we are making any changes or not. Return true if
nothing is actually changing. */
ZERO_STRUCT(old_param);
if (get_specific_param(*printer, 2, param->value, &old_param.data,
&old_param.type, &old_param.data_len)) {
if (param->type == old_param.type &&
param->data_len == old_param.data_len &&
memcmp(param->data, old_param.data,
old_param.data_len) == 0) {
DEBUG(3, ("setprinterdata hasn't changed\n"));
status = NT_STATUS_NO_PROBLEMO;
goto done;
}
}
/* Access check */
if (!print_access_check(NULL, snum, PRINTER_ACCESS_ADMINISTER)) {
DEBUG(3, ("security descriptor change denied by existing "
"security descriptor\n"));
status = ERROR_ACCESS_DENIED;
goto done;
}
unlink_specific_param_if_exist(printer->info_2, param);
if (!add_a_specific_param(printer->info_2, param))
@ -5022,7 +5249,10 @@ uint32 _spoolss_setprinterdata( POLICY_HND *handle,
else
status = mod_a_printer(*printer, 2);
done:
free_a_printer(&printer, 2);
safe_free(old_param.data);
return status;
}