Screen grabs from an SUID program don't work?

was: PtInit() doesn’t work from SUID root program.

In a nutshell, I have the need to do an automatic screen grab from an
SUID program that’s run by a normal user. If the program is run with
normal permissions (i.e. chmod 0755), the screen grab works fine.
However, if the program is run SUID (i.e. chmod 4755), the program does
not create any files. Here’s the example code that shows the issue:

//BEGIN EXAMPLE CODE//
//to compile: qcc octopus.c -o octopus -lph -lphexlib

#include <iostream.h>
#include <stdio.h>
#include <Pt.h>

// make sure we include BMP support!
#define PX_IMAGE_MODULES
#define PX_PHIMAGE_SUPPORT
#define PX_BMP_SUPPORT
#include <photon/PxImage.h>

int print(PhImage_t *image)
{
int iErrCode = 0;
PhDrawContext_t pdc = 0;
PpPrintContext_t
mpPC;
PhPoint_t p = { 0, 0 };

mpPC = PpCreatePC();

if (mpPC)
{
// set up my context
if(!iErrCode)
{
PpSetPC(mpPC, Pp_PC_DRIVER, “/usr/bin/phs-to-ps”, 0);
PpSetPC(mpPC, Pp_PC_FILENAME, “grabscreen.phs”, 0);

Ph_rect xPrintArea;
xPrintArea.ul.x = 750;
xPrintArea.ul.y = 500;
xPrintArea.lr.x = 500;
xPrintArea.lr.y = 400;
PpSetPC(mpPC, Pp_PC_MARGINS, &xPrintArea, 0);

PhDim_t xPaperSize;
xPaperSize.w = 8500;
xPaperSize.h = 11000;
PpSetPC(mpPC, Pp_PC_PAPER_SIZE, &xPaperSize, 0);

PhDim_t xSourceSize;
xSourceSize.w = 1024;
xSourceSize.h = 768;
PpSetPC(mpPC, Pp_PC_SOURCE_SIZE, &xSourceSize, 0);

}

// initialize the print job
if(!iErrCode)
{
if(PpStartJob(mpPC))
iErrCode = 2;
}

// make the print context active
if(!iErrCode)
{
pdc = PpContinueJob(mpPC);
if(!pdc)
iErrCode = 3;
}

if (image == NULL)
{
cout << “ERROR: NULL image” << endl;
exit(1);
}

cout << "image size: " << image->size.w << “x” << image->size.h << endl;

PgSetPalette( image->palette, 0, 0, image->colors,
Pg_PALSET_SOFT, 0 );

iErrCode = PgDrawImage( image->image, image->type, &p,
&image->size, image->bpl, 0 );

cout << "iErrCode = " << iErrCode << endl;

PpEndJob(mpPC);
PpReleasePC(mpPC);
}

}

int snap(char *fname,PhRect_t rect)
{
int len;
char *mem;
PhImage_t *image;

// get the size of the shared memory you need for the image
if ((len=PgReadScreenSize(&rect))<=0) return -1;

// allocate shared memory to contain the image
if (!(mem=(char*)PgShmemCreate(len,NULL))) return -1;

// read the portion of the screen you’re interested in
if (image=PgReadScreen(&rect,mem))
{
// save the image to the file name given
PxWriteImage(fname,image,NULL,PX_IMAGE_BMP,0);

if ((image = PxLoadImage( fname, NULL )) == NULL) {
perror( “Unable to load image” );
return -1;
}

print(image);
}

// destroy the shared memory you used
PgShmemDestroy(mem);

return 0;
}

int main(int argc,char *argv[])
{
int i;
if (argc!=2)
{
printf("Usage: grab ");
exit(-1);
}

int iPtInit = PtInit("/dev/photon");

cout << "PtInit returned: " << iPtInit << endl;

// get a connection to Photon
if (iPtInit!=0)
{
perror(“Error: Could not connect to Photon.”);
exit(-1);
}

PhRect_t rect;
char fname[120];

//windows_rect(r,&rect);
rect.ul.x = 0;
rect.ul.y = 0;
rect.lr.x = 1023;
rect.lr.y = 767;
sprintf(fname,argv[1],i);
printf(“saving ‘%s’\n”,fname);
snap(fname,rect);

return 0;
}

Under 6.2.1, this program works properly for me either way, and creates the
output file with ownership equal to the effective user (root or mortal,
depending on the 4000 bit).

But if this is supposed to be a model of a real application, why don’t you
seteuid() to the real user for everything except the part that really needs
the root privileges? Presumably the PtInit() call belongs in the
unprivileged part, so I make this suggestion now as what I think you should
be doing anyway, not just as a bug workaround.

dB

“Mathew Kirsch” <mkirsch@ocdus.jnj.com> wrote in message
news:b358rs$a1r$1@inn.qnx.com

was: PtInit() doesn’t work from SUID root program.

In a nutshell, I have the need to do an automatic screen grab from an
SUID program that’s run by a normal user. If the program is run with
normal permissions (i.e. chmod 0755), the screen grab works fine.
However, if the program is run SUID (i.e. chmod 4755), the program does
not create any files. Here’s the example code that shows the issue:

file://BEGIN EXAMPLE CODE//
file://to compile: qcc octopus.c -o octopus -lph -lphexlib

#include <iostream.h
#include <stdio.h
#include <Pt.h

// make sure we include BMP support!
#define PX_IMAGE_MODULES
#define PX_PHIMAGE_SUPPORT
#define PX_BMP_SUPPORT
#include <photon/PxImage.h

int print(PhImage_t *image)
{
int iErrCode = 0;
PhDrawContext_t pdc = 0;
PpPrintContext_t
mpPC;
PhPoint_t p = { 0, 0 };

mpPC = PpCreatePC();

if (mpPC)
{
// set up my context
if(!iErrCode)
{
PpSetPC(mpPC, Pp_PC_DRIVER, “/usr/bin/phs-to-ps”, 0);
PpSetPC(mpPC, Pp_PC_FILENAME, “grabscreen.phs”, 0);

Ph_rect xPrintArea;
xPrintArea.ul.x = 750;
xPrintArea.ul.y = 500;
xPrintArea.lr.x = 500;
xPrintArea.lr.y = 400;
PpSetPC(mpPC, Pp_PC_MARGINS, &xPrintArea, 0);

PhDim_t xPaperSize;
xPaperSize.w = 8500;
xPaperSize.h = 11000;
PpSetPC(mpPC, Pp_PC_PAPER_SIZE, &xPaperSize, 0);

PhDim_t xSourceSize;
xSourceSize.w = 1024;
xSourceSize.h = 768;
PpSetPC(mpPC, Pp_PC_SOURCE_SIZE, &xSourceSize, 0);

}

// initialize the print job
if(!iErrCode)
{
if(PpStartJob(mpPC))
iErrCode = 2;
}

// make the print context active
if(!iErrCode)
{
pdc = PpContinueJob(mpPC);
if(!pdc)
iErrCode = 3;
}

if (image == NULL)
{
cout << “ERROR: NULL image” << endl;
exit(1);
}

cout << "image size: " << image->size.w << “x” << image->size.h << endl;

PgSetPalette( image->palette, 0, 0, image->colors,
Pg_PALSET_SOFT, 0 );

iErrCode = PgDrawImage( image->image, image->type, &p,
&image->size, image->bpl, 0 );

cout << "iErrCode = " << iErrCode << endl;

PpEndJob(mpPC);
PpReleasePC(mpPC);
}

}

int snap(char *fname,PhRect_t rect)
{
int len;
char *mem;
PhImage_t *image;

// get the size of the shared memory you need for the image
if ((len=PgReadScreenSize(&rect))<=0) return -1;

// allocate shared memory to contain the image
if (!(mem=(char*)PgShmemCreate(len,NULL))) return -1;

// read the portion of the screen you’re interested in
if (image=PgReadScreen(&rect,mem))
{
// save the image to the file name given
PxWriteImage(fname,image,NULL,PX_IMAGE_BMP,0);

if ((image = PxLoadImage( fname, NULL )) == NULL) {
perror( “Unable to load image” );
return -1;
}

print(image);
}

// destroy the shared memory you used
PgShmemDestroy(mem);

return 0;
}

int main(int argc,char *argv[])
{
int i;
if (argc!=2)
{
printf("Usage: grab ");
exit(-1);
}

int iPtInit = PtInit("/dev/photon");

cout << "PtInit returned: " << iPtInit << endl;

// get a connection to Photon
if (iPtInit!=0)
{
perror(“Error: Could not connect to Photon.”);
exit(-1);
}

PhRect_t rect;
char fname[120];

file://windows_rect(r,&rect);
rect.ul.x = 0;
rect.ul.y = 0;
rect.lr.x = 1023;
rect.lr.y = 767;
sprintf(fname,argv[1],i);
printf(“saving ‘%s’\n”,fname);
snap(fname,rect);

return 0;
}

David Bacon wrote:

Under 6.2.1, this program works properly for me either way, and creates the
output file with ownership equal to the effective user (root or mortal,
depending on the 4000 bit).

So you’re saying that my issue is probably fixed by 6.2.1?

But if this is supposed to be a model of a real application, why don’t you
seteuid() to the real user for everything except the part that really needs
the root privileges? Presumably the PtInit() call belongs in the
unprivileged part, so I make this suggestion now as what I think you should
be doing anyway, not just as a bug workaround.

I’ve played with seteuid() in the real application, and it makes
absolutely no difference in the way the program runs. The files are
still created, ownership set to the mortal user. The process is still
owned by the mortal user’s UID. The screengrab function still does not work.

The solution:

I needed to add /usr/photon/dll to the LD_LIBRARY_PATH in my kernel
build file. Works like a charm, thanks to Dave B. of QNX.