summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2014-02-07 01:48:59 +0100
committerSimon Rettberg2014-02-07 01:48:59 +0100
commit8ae112083bbe26848f41d09c95559acb96b3ccb0 (patch)
treec9e4408acff35a608e41540e221c4232fb1384a6
parentLots of changes: Two binaries (selection/pw auth), qmake -> cmake, gitignore,... (diff)
downloadprintergui-8ae112083bbe26848f41d09c95559acb96b3ccb0.tar.gz
printergui-8ae112083bbe26848f41d09c95559acb96b3ccb0.tar.xz
printergui-8ae112083bbe26848f41d09c95559acb96b3ccb0.zip
Getting there, slowly:
- Check if the backend is invoked as a result of the printergui sending a printjob If not, just relay to the real backend and appear transparent If yes, copy environment from the printergui (X access etc) and show the username/password dialog. - Drop privileges whereever neccessary (when invoking backend, when showing GUI) TODO: Use pipe to send user/pass to backend from the GUI. Make OK and Cancel in the GUI work.
-rw-r--r--src/maingui/printergui.cpp4
-rw-r--r--src/pwgui/main.cpp200
-rw-r--r--src/pwgui/pwgui.cpp18
-rw-r--r--src/pwgui/pwgui.h3
4 files changed, 187 insertions, 38 deletions
diff --git a/src/maingui/printergui.cpp b/src/maingui/printergui.cpp
index 3744ffc..90d4731 100644
--- a/src/maingui/printergui.cpp
+++ b/src/maingui/printergui.cpp
@@ -214,11 +214,13 @@ void PrinterGui::on_buttonPrint_clicked()
}
cupsSetUser(this->user);
+ char jobtitle[100];
+ snprintf(jobtitle, 100, "gui-%d", (int)getpid());
// Drucken
if( 0 == cupsPrintFile(dest->name,
file,
- NULL,
+ jobtitle,
dest->num_options,
dest->options)) {
QMessageBox::critical(this, "CUPS Fehler", cupsLastErrorString());
diff --git a/src/pwgui/main.cpp b/src/pwgui/main.cpp
index d75f280..c33b3f6 100644
--- a/src/pwgui/main.cpp
+++ b/src/pwgui/main.cpp
@@ -10,12 +10,29 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <signal.h>
+#include <limits.h>
+#include <pwd.h>
+#include <grp.h>
#define NAMELEN 400
#define BUFLEN 1000
+static int pid = -1;
+// UID and GUI of user we should drop privileges to
+static int ruid = 65534, rgid = 65534;
+static char ruser[NAMELEN] = "";
+// Copy of the GUIs environment, so we can access X
+// Whatever you do, make sure this has at least two nullchars at the end!
+static char gui_env[BUFLEN] = "\0\0\0";
+
static int run_backend(char *backend, char *uri, char *jobid, char *user, char *title, char *copies, char *options, char *file, char *password);
+static bool helper_getpiduid(char *user, char *title);
+static bool helper_loadlpuser();
+static void helper_dropprivs();
+static void helper_copyenv();
+
int main(int argc, char *argv[])
{
char tmpfile[NAMELEN];
@@ -35,6 +52,41 @@ int main(int argc, char *argv[])
return CUPS_BACKEND_FAILED;
}
+ // Determine device uri
+ char *env = getenv("DEVICE_URI");
+ if (env != NULL && strchr(env, ':') != NULL) {
+ snprintf(device, NAMELEN, "%s", env);
+ } else if (strstr(argv[0], ":/") != NULL) {
+ snprintf(device, NAMELEN, "%s", argv[0]);
+ } else {
+ fputs("ERROR: No device URI given.\n", stderr);
+ return CUPS_BACKEND_FAILED;
+ }
+
+ // Get backend from uri
+ char *colon = strchr(device, ':');
+ *colon = '\0';
+ snprintf(backend, NAMELEN, "%s/%s", BACKEND_PATH, device);
+ *colon = ':';
+ // Is valid?
+ if (access(backend, X_OK | R_OK) != 0) {
+ fprintf(stderr, "ERROR: Backend %s is not executable.\n", backend);
+ return CUPS_BACKEND_FAILED;
+ }
+
+ // argv[3] is title, get printergui pid from it
+ if (!helper_getpiduid(argv[2], argv[3])) {
+ // El cheapo validation failed. Don't enable "smart mode" (GUI etc), just exec real backend
+ // Mimic cups behaviour wrt dropping privs (Only 0700 or 0500 == root)
+ helper_loadlpuser();
+ struct stat st;
+ if (stat(backend, &st) != 0 || (st.st_mode & 0077) != 0) helper_dropprivs();
+ execv(backend, argv);
+ exit(127);
+ }
+ kill(pid, SIGTERM);
+
+ // Get document to print
if (argc == 6) {
// Data comes from stdin, save...
snprintf(tmpfile, NAMELEN, "/tmp/print-%s-%d-%s-%d", argv[1], (int)time(NULL), argv[2], (int)getpid());
@@ -68,38 +120,48 @@ int main(int argc, char *argv[])
//
}
- // Determine device uri
- char *env = getenv("DEVICE_URI");
- if (env != NULL && strchr(env, ':') != NULL) {
- snprintf(device, NAMELEN, "%s", env);
- } else if (strstr(argv[0], ":/") != NULL) {
- snprintf(device, NAMELEN, "%s", argv[0]);
- } else {
- fputs("ERROR: No device URI given.\n", stderr);
- return CUPS_BACKEND_FAILED;
- }
-
- // Get backend from uri
- char *colon = strchr(device, ':');
- *colon = '\0';
- snprintf(backend, NAMELEN, "%s/%s", BACKEND_PATH, device);
- *colon = ':';
- // Is valid?
- if (access(backend, X_OK | R_OK) != 0) {
- fprintf(stderr, "ERROR: Backend %s is not executable.\n", backend);
- return CUPS_BACKEND_FAILED;
- }
-
// Try right away with what we got
spoolres = run_backend(backend, device, argv[1], argv[2], argv[3], argv[4], argv[5], tmpfile, NULL);
- if (spoolres == CUPS_BACKEND_OK) return spoolres; // Yay
-
- // Dialog - TODO: Drop privileges here!
- QApplication a(argc, argv);
- PwGui w(NULL);
- w.show();
- return a.exec();
- // TODO .....
+ if (spoolres != CUPS_BACKEND_AUTH_REQUIRED) return spoolres; // Yay
+
+ // Seems we need the dialog
+ int status;
+ do {
+ int pfd[2];
+ if (pipe(pfd) != 0) {
+ fputs("ERROR: Could not create pipe for GUI.\n", stderr);
+ return CUPS_BACKEND_FAILED;
+ }
+ pid_t pid = fork();
+ if (pid == 0) {
+ // Child - GUI
+ close(pfd[0]);
+ helper_dropprivs();
+ helper_copyenv();
+ QApplication a(argc, argv);
+ PwGui w(pfd[1]);
+ w.show();
+ exit(a.exec());
+ return CUPS_BACKEND_FAILED;
+ }
+ // Main (Parent)
+ close(pfd[1]);
+ // Read from pipe
+ char creds[NAMELEN], *pass = NULL;
+ int bytes = read(pfd[0], creds, NAMELEN - 1);
+ // Wait for child to die
+ waitpid(pid, NULL, 0); // Don't check status, just look at pipe data
+ if (bytes <= 0) {
+ fputs("ERROR: Could not read anything from pipe after showing GUI.\n", stderr);
+ return CUPS_BACKEND_CANCEL;
+ }
+ creds[bytes] = '\0';
+ int len = strlen(creds);
+ if (len < bytes) pass = creds + len + 1;
+ // Run backend with pimped user/pass
+ status = run_backend(backend, device, argv[1], creds, argv[3], argv[4], argv[5], tmpfile, pass);
+ } while (status != CUPS_BACKEND_OK);
+ return CUPS_BACKEND_OK;
}
static int run_backend(char *backend, char *uri, char *jobid, char *user, char *title, char *copies, char *options, char *file, char *password)
@@ -118,6 +180,10 @@ static int run_backend(char *backend, char *uri, char *jobid, char *user, char *
args[5] = options;
args[6] = file;
args[7] = NULL;
+ // Priv dropping
+ struct stat st;
+ if (stat(backend, &st) != 0 || (st.st_mode & 0077) != 0) helper_dropprivs();
+ // Exec
execv(backend, args);
exit(127);
return 127;
@@ -133,3 +199,77 @@ static int run_backend(char *backend, char *uri, char *jobid, char *user, char *
return WEXITSTATUS(status);
}
+static bool helper_getpiduid(char *user, char *title)
+{
+ // it has to be gui-<PID>, PID has to be an instance of printergui
+ // and we have to be able to kill it, only then we assume we should bother the user
+ // with an authentication dialog
+ if (strncmp(title, "gui-", 4) != 0) return false; // Wrong job title
+ struct stat st;
+ struct passwd *pw = getpwnam(user);
+ if (pw == NULL) return false;
+ int p = atoi(title + 4);
+ char bin[PATH_MAX+1], tmp[100];
+ snprintf(tmp, 100, "/proc/%d/exe", p);
+ if (realpath(tmp, bin) == NULL) return false;
+ char *last = strrchr(bin, '/');
+ if (last == NULL || strcmp(last, "/printergui") != 0) return false; // Wrong process
+ // PID passed via job title seems to be the printergui
+ if (lstat(tmp, &st) < 0) return false;
+ if (st.st_uid != pw->pw_uid) return false; // Print job user doesn't match printergui process owner
+ // All checks passed, make stuff global
+ pid = p;
+ ruid = pw->pw_uid;
+ rgid = pw->pw_gid;
+ snprintf(ruser, NAMELEN, "%s", user);
+ // Finally, try to copy the environment of the process
+ snprintf(tmp, 100, "/proc/%d/environ", p);
+ int fh = open(tmp, O_RDONLY);
+ if (fh >= 0) {
+ int bytes = read(fh, gui_env, BUFLEN - 2);
+ close(fh);
+ if (bytes >= 0) {
+ gui_env[bytes+0] = '\0';
+ gui_env[bytes+1] = '\0';
+ }
+ }
+ return true;
+}
+
+static bool helper_loadlpuser()
+{
+ struct passwd *pw = getpwnam("lp");
+ if (pw == NULL) return false;
+ ruid = pw->pw_uid;
+ rgid = pw->pw_gid;
+ return true;
+}
+
+static void helper_dropprivs()
+{
+ if (ruid == 0) return;
+ initgroups(ruser, rgid);
+ setgid(rgid);
+ setuid(ruid);
+ chdir("/");
+ if (setuid(0) != -1) {
+ fputs("ERROR: setuid-fu!?\n", stderr);
+ exit(CUPS_BACKEND_FAILED);
+ }
+}
+
+static void helper_copyenv()
+{
+ char *ptr = gui_env;
+ while (strlen(ptr) > 0) {
+ char *equal = strchr(ptr, '=');
+ if (equal != NULL) {
+ char *value = equal + 1;
+ *equal = '\0';
+ setenv(ptr, value, 1);
+ *equal = '=';
+ }
+ ptr += strlen(ptr) + 1;
+ }
+}
+
diff --git a/src/pwgui/pwgui.cpp b/src/pwgui/pwgui.cpp
index 31dd822..448cee8 100644
--- a/src/pwgui/pwgui.cpp
+++ b/src/pwgui/pwgui.cpp
@@ -5,20 +5,24 @@
#include <QDesktopWidget>
// ____________________________________________________________________________
-PwGui::PwGui(QWidget *parent) :
+PwGui::PwGui(int pfd, QWidget *parent) :
QMainWindow(parent),
- ui(new Ui::PwGui) {
+ ui(new Ui::PwGui),
+ pipefd(pfd)
+{
// Initialize UI
initializeUI();
}
// ____________________________________________________________________________
-PwGui::~PwGui() {
+PwGui::~PwGui()
+{
delete ui;
}
// ____________________________________________________________________________
-void PwGui::initializeUI() {
+void PwGui::initializeUI()
+{
ui->setupUi(this);
/*
@@ -43,14 +47,16 @@ void PwGui::initializeUI() {
}
/*
-void PwGui::on_buttonCancel_clicked() {
+void PwGui::on_buttonCancel_clicked()
+{
// Quit with code 1
QCoreApplication::instance()->exit(1);
}
*/
// ____________________________________________________________________________
-void PwGui::on_checkStatusTimer() {
+void PwGui::on_checkStatusTimer()
+{
static int retries = 0;
if (++retries > 20) {
QCoreApplication::instance()->quit();
diff --git a/src/pwgui/pwgui.h b/src/pwgui/pwgui.h
index 3e56ec8..95a7a05 100644
--- a/src/pwgui/pwgui.h
+++ b/src/pwgui/pwgui.h
@@ -16,7 +16,7 @@ class PwGui : public QMainWindow
Q_OBJECT
public:
- explicit PwGui(QWidget *parent = 0);
+ explicit PwGui(int pfd, QWidget *parent = 0);
~PwGui();
private slots:
@@ -26,6 +26,7 @@ private:
Ui::PwGui *ui;
void initializeUI();
QTimer* checkStatusTimer;
+ int pipefd;
};
#endif // AUTHENTICATION_H