Skip to content
Snippets Groups Projects
Commit 9d3cbde7 authored by Lennart Kramer's avatar Lennart Kramer
Browse files

send raw uid/gid to maxima process

this no longer hardcodes the number of processes and is in preparation for
rootless operation
parent b40d2a91
No related branches found
No related tags found
No related merge requests found
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <pwd.h>
#include <signal.h>
#include <sys/resource.h>
......@@ -10,14 +8,9 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <bsd/unistd.h>
#include <limits.h>
#include <grp.h>
// number of processes
#ifndef N_SLOT
#define N_SLOT 32
#endif
// maximum number of open file descriptors
#ifndef RNOFILE
......@@ -40,33 +33,6 @@ char filepath[FILEPATH_LEN];
// redirects input/output, creates temporary subdirectories
char *fork_new_process() {
fflush(stdout);
uid_t user_id[N_SLOT];
gid_t group_id[N_SLOT];
// cache and verify uids/gids on startup
for (int i = 1; i <= N_SLOT; i++) {
char username[16];
int len = snprintf(username, 15, "maxima-%d", i);
if (len < 0 || len > 15) {
dprintf(STDERR_FILENO, "Internal error getting user name\n");
return NULL;
}
struct passwd *userinfo = getpwnam(username);
if (!userinfo) {
dprintf(STDERR_FILENO, "Could not read user information for %s: %s\n",
username, strerror(errno));
return NULL;
}
uid_t uid = userinfo->pw_uid;
gid_t gid = userinfo->pw_gid;
if (uid == 0 || gid == 0) {
dprintf(STDERR_FILENO, "maxima-%d is root, quitting\n", i);
return NULL;
}
user_id[i - 1] = uid;
group_id[i - 1] = gid;
}
// send an S for Synchronization, so that
// the server process doesn't accidentally write into
......@@ -101,25 +67,39 @@ char *fork_new_process() {
for (;;) {
// can't flush enough
fflush(stdout);
int slot;
// the slot number and temp directory is sent to the process
// over stdin in the format "%d%s", where %s can contain anything
// the uid/gid and temp directory is sent to the process
// over stdin in the format "%d:%d%s", where %s can contain anything
// but newlines and musn't start with a number, which isn't a
// problem for absolute paths
if (scanf("%d", &slot) == EOF) {
if (errno != 0) {
perror("Error getting slot number from stdin");
char *input = fgets(filepath, FILEPATH_LEN, stdin);
if (!input) {
perror("Error getting input");
ret = NULL;
break;
}
char *cur;
long uid = strtol(input, &cur, 10);
if (cur == input) {
fprintf(stderr, "Invalid uid\n");
ret = NULL;
break;
}
if (*cur++ != ':') {
fprintf(stderr, "Invalid input\n");
ret = NULL;
break;
}
char *tempdir = fgets(filepath, FILEPATH_LEN, stdin);
if (!tempdir) {
perror("Error getting temp path name");
input = cur;
long gid = strtol(input, &cur, 10);
if (cur == input) {
fprintf(stderr, "Invalid gid\n");
ret = NULL;
break;
}
char *tempdir = cur;
// remove the last newline, if it exists
size_t last_char = strlen(tempdir) - 1;
if (tempdir[last_char] == '\n') {
......@@ -138,15 +118,19 @@ char *fork_new_process() {
continue;
}
// verify valid slot number
if (slot <= 0 || slot > N_SLOT) {
dprintf(STDERR_FILENO, "Invalid slot number: %d\n", slot);
// valididate uid and gid
if (uid <= 0 || uid > 0xffff) {
dprintf(STDERR_FILENO, "Invalid uid: %ld\n", uid);
ret = NULL;
break;
}
if (gid <= 0 || gid > 0xffff) {
dprintf(STDERR_FILENO, "Invalid gid: %ld\n", gid);
ret = NULL;
break;
}
uid_t uid = user_id[slot - 1];
gid_t gid = group_id[slot - 1];
// note: setgid should be executed before setuid when dropping from root
if (setgid(gid) == -1) {
perror("Could not set gid");
......@@ -168,6 +152,13 @@ char *fork_new_process() {
break;
}
// start new process group
if (setpgid(0, 0) == -1) {
perror("Could not set pgid");
ret = NULL;
break;
}
if (chdir(tempdir) == -1) {
perror("Could not chdir to temporary directory");
ret = NULL;
......
This diff is collapsed.
......@@ -7,7 +7,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"io/fs"
"log"
"net/http"
"os"
......@@ -156,12 +156,12 @@ func new_mother_proc(binpath string, libs []string) (*MotherProcess, error) {
func (p *MotherProcess) spawn_new(user *User) (*ChildProcess, float64, error) {
start := time.Now()
var result ChildProcess
var child ChildProcess
err := user.sync_execute_as(MAXIMA_SPAWN_TIMEOUT, func(err error) error {
if err != nil {
return err
}
tmp_dir, err := ioutil.TempDir(tmp_prefix, "maxima-plot-")
tmp_dir, err := os.MkdirTemp(tmp_prefix, "maxima-plot-")
if err != nil {
return fmt.Errorf("unable to create temp dir: %s", err)
}
......@@ -187,7 +187,7 @@ func (p *MotherProcess) spawn_new(user *User) (*ChildProcess, float64, error) {
return fmt.Errorf("could not create named pipe in temp folder: %s", err)
}
_, err = fmt.Fprintf(p.Input, "%d%s\n", user.Id, tmp_dir)
_, err = fmt.Fprintf(p.Input, "%d:%d%s\n", user.Uid, user.Gid, tmp_dir)
if err != nil {
return fmt.Errorf("unable to communicate with process: %s", err)
}
......@@ -204,10 +204,10 @@ func (p *MotherProcess) spawn_new(user *User) (*ChildProcess, float64, error) {
return fmt.Errorf("could not open temp dir inpipe: %s", err)
}
result.User = user
result.Input = in
result.Outfile = out
result.TempDir = tmp_dir
child.User = user
child.Input = in
child.Outfile = out
child.TempDir = tmp_dir
return nil
})
if err != nil {
......@@ -215,21 +215,21 @@ func (p *MotherProcess) spawn_new(user *User) (*ChildProcess, float64, error) {
}
debugf("Debug: Attempting to read from child")
err = result.Outfile.SetReadDeadline(time.Now().Add(MAXIMA_SPAWN_TIMEOUT))
err = child.Outfile.SetReadDeadline(time.Now().Add(MAXIMA_SPAWN_TIMEOUT))
if err != nil {
return nil, 0.0, fmt.Errorf("unable to set read deadline: %s", err)
}
bufout := bufio.NewReader(result.Outfile)
bufout := bufio.NewReader(child.Outfile)
_, err = fmt.Fscanf(bufout, "\nT")
if err != nil {
return nil, 0.0, fmt.Errorf("not able to find end marker: %s", err)
}
result.Output = bufout
child.Output = bufout
total := time.Since(start)
total_ms := float64(total.Microseconds()) / 1000
debugf("Debug: child took %s for startup", total)
return &result, total_ms, nil
return &child, total_ms, nil
}
type MaximaResponse struct {
......@@ -349,7 +349,7 @@ func (req *MaximaRequest) WriteResponseWithoutPlots(response MaximaResponse) {
req.Metrics.NumSuccess.Inc()
}
func (req *MaximaRequest) WriteResponseWithPlots(response MaximaResponse, output_dir string, plots_output []os.FileInfo) {
func (req *MaximaRequest) WriteResponseWithPlots(response MaximaResponse, output_dir string, plots_output []fs.DirEntry) {
debugf("Debug: output (%d) is zip, OUTPUT len %d", req.User.Id, response.Response.Len())
req.W.Header().Set("Content-Type", "application/zip;charset=UTF-8")
zipfile := zip.NewWriter(req.W)
......@@ -420,7 +420,7 @@ func (req *MaximaRequest) WriteResponse(response MaximaResponse) {
output_dir := filepath.Clean(filepath.Join(req.Proc.TempDir, "output")) + "/"
// if there are any files inside the output dir, we give back a zip file containing all output
// files and the command output inside a file named OUTPUT
plots_output, err := ioutil.ReadDir(output_dir)
plots_output, err := os.ReadDir(output_dir)
if err != nil {
// just return text if directory could not be read and assume no plots were generated
req.log_with_input("Warn: could not read temp directory of maxima process: %s", err)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment