damn. We need root privilages to do semaphore operations even if we
[kai/samba.git] / source3 / locking / shmem_sysv.c
index 793e508c7193b71d217c1e3132cfa6c328a3c7f5..8832902820a23bb2172430d84750006fea79d48d 100644 (file)
@@ -33,7 +33,27 @@ extern int DEBUGLEVEL;
 #define SHM_MAGIC 0x53484100
 #define SHM_VERSION 2
 
+#ifdef SHM_R
 #define IPC_PERMS ((SHM_R | SHM_W) | (SHM_R>>3) | (SHM_R>>6))
+#else
+#define IPC_PERMS 0644
+#endif
+
+#ifdef SECURE_SEMAPHORES
+/* secure semaphores are slow because we have to do a become_root()
+   on every call! */
+#define SEMAPHORE_PERMS IPC_PERMS
+#else
+#define SEMAPHORE_PERMS 0666
+#endif
+
+#ifdef SEMMSL
+#define SHMEM_HASH_SIZE (SEMMSL-1)
+#else
+#define SHMEM_HASH_SIZE 63
+#endif
+
+#define MIN_SHM_SIZE 10240
 
 static int shm_id;
 static int sem_id;
@@ -80,45 +100,49 @@ static BOOL shm_initialize_called = False;
 
 static int read_only;
 
-static BOOL sem_lock(int i)
+static BOOL sem_change(int i, int op)
 {
+#ifdef SECURE_SEMAPHORES
+       extern struct current_user current_user;
+       int became_root=0;
+#endif
        struct sembuf sb;
+       int ret;
+
        if (read_only) return True;
-       
-       sb.sem_num = i;
-       sb.sem_op = -1;
-       sb.sem_flg = SEM_UNDO;
 
-       if (semop(sem_id, &sb, 1) != 0) {
-               DEBUG(0,("ERROR: IPC lock failed on semaphore %d\n", i));
-               return False;
+#ifdef SECURE_SEMAPHORES
+       if (current_user.uid != 0) {
+               become_root(0);
+               became_root = 1;
        }
-
-       return True;
-}
-
-static BOOL sem_unlock(int i)
-{
-       struct sembuf sb;
-       if (read_only) return True;
+#endif
 
        sb.sem_num = i;
-       sb.sem_op = 1;
+       sb.sem_op = op;
        sb.sem_flg = SEM_UNDO;
 
-       if (semop(sem_id, &sb, 1) != 0) {
-               DEBUG(0,("ERROR: IPC unlock failed on semaphore %d\n", i));
-               return False;
+       ret = semop(sem_id, &sb, 1);
+
+       if (ret != 0) {
+               DEBUG(0,("ERROR: sem_change(%d,%d) failed (%s)\n", 
+                        i, op, strerror(errno)));
        }
 
-       return True;
+#ifdef SECURE_SEMAPHORES
+       if (became_root) {
+               unbecome_root(0);
+       }
+#endif
+
+       return ret == 0;
 }
 
 static BOOL global_lock(void)
 {
        global_lock_count++;
        if (global_lock_count == 1)
-               return sem_lock(0);
+               return sem_change(0, -1);
        return True;
 }
 
@@ -126,7 +150,7 @@ static BOOL global_unlock(void)
 {
        global_lock_count--;
        if (global_lock_count == 0)
-               return sem_unlock(0);
+               return sem_change(0, 1);
        return True;
 }
 
@@ -449,8 +473,7 @@ static int shm_get_userdef_off(void)
   ******************************************************************/
 static BOOL shm_lock_hash_entry(unsigned int entry)
 {
-       DEBUG(0,("hash lock %d\n", entry));
-       return sem_lock(entry+1);
+       return sem_change(entry+1, -1);
 }
 
 /*******************************************************************
@@ -458,8 +481,7 @@ static BOOL shm_lock_hash_entry(unsigned int entry)
   ******************************************************************/
 static BOOL shm_unlock_hash_entry(unsigned int entry)
 {
-       DEBUG(0,("hash unlock %d\n", entry));
-       return sem_unlock(entry+1);
+       return sem_change(entry+1, 1);
 }
 
 
@@ -483,6 +505,16 @@ static BOOL shm_get_usage(int *bytes_free,
        return True;
 }
 
+
+/*******************************************************************
+hash a number into a hash_entry
+  ******************************************************************/
+static unsigned shm_hash_size(void)
+{
+       return hash_size;
+}
+
+
 static struct shmem_ops shmops = {
        shm_close,
        shm_alloc,
@@ -493,12 +525,13 @@ static struct shmem_ops shmops = {
        shm_lock_hash_entry,
        shm_unlock_hash_entry,
        shm_get_usage,
+       shm_hash_size,
 };
 
 /*******************************************************************
   open the shared memory
   ******************************************************************/
-struct shmem_ops *sysv_shm_open(int size, int ronly)
+struct shmem_ops *sysv_shm_open(int ronly)
 {
        BOOL created_new = False;
        BOOL other_processes;
@@ -509,17 +542,23 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
 
        read_only = ronly;
 
-       shm_size = size;
+       shm_size = lp_shmem_size();
 
-       DEBUG(4,("Trying sysv shmem open of size %d\n", size));
+       DEBUG(4,("Trying sysv shmem open of size %d\n", shm_size));
 
        /* first the semaphore */
        sem_id = semget(SEMAPHORE_KEY, 0, 0);
        if (sem_id == -1) {
                if (read_only) return NULL;
 
-               sem_id = semget(SEMAPHORE_KEY, lp_shmem_hash_size()+1, 
-                               IPC_CREAT | IPC_EXCL | IPC_PERMS);
+               hash_size = SHMEM_HASH_SIZE;
+
+               while (hash_size > 1) {
+                       sem_id = semget(SEMAPHORE_KEY, hash_size+1, 
+                                       IPC_CREAT|IPC_EXCL| SEMAPHORE_PERMS);
+                       if (sem_id != -1 || errno != EINVAL) break;
+                       hash_size--;
+               }
 
                if (sem_id == -1) {
                        DEBUG(0,("Can't create or use semaphore %s\n", 
@@ -528,7 +567,7 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
 
                if (sem_id != -1) {
                        su.val = 1;
-                       for (i=0;i<lp_shmem_hash_size()+1;i++) {
+                       for (i=0;i<hash_size+1;i++) {
                                if (semctl(sem_id, i, SETVAL, su) != 0) {
                                        DEBUG(1,("Failed to init semaphore %d\n", i));
                                }
@@ -548,10 +587,21 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
        if (semctl(sem_id, 0, IPC_STAT, su) != 0) {
                DEBUG(0,("ERROR shm_open : can't IPC_STAT\n"));
        }
-       hash_size = sem_ds.sem_nsems;
-       if (hash_size != lp_shmem_hash_size()+1) {
-               DEBUG(0,("WARNING: nsems=%d\n", hash_size));
+       hash_size = sem_ds.sem_nsems-1;
+
+       if (!read_only) {
+               if (sem_ds.sem_perm.cuid != 0 || sem_ds.sem_perm.cgid != 0) {
+                       DEBUG(0,("ERROR: root did not create the semaphore\n"));
+                       return NULL;
+               }
+
+               sem_ds.sem_perm.mode = SEMAPHORE_PERMS;
+               if (semctl(sem_id, 0, IPC_SET, su) != 0) {
+                       DEBUG(0,("ERROR shm_open : can't IPC_SET\n"));
+               }
        }
+
+       
        
        if (!global_lock())
                return NULL;
@@ -562,7 +612,12 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
        /* if that failed then create one */
        if (shm_id == -1) {
                if (read_only) return NULL;
-               shm_id = shmget(SHMEM_KEY, shm_size, IPC_CREAT | IPC_EXCL);
+               while (shm_size > MIN_SHM_SIZE) {
+                       shm_id = shmget(SHMEM_KEY, shm_size, 
+                                       IPC_CREAT | IPC_EXCL | IPC_PERMS);
+                       if (shm_id != -1 || errno != EINVAL) break;
+                       shm_size *= 0.9;
+               }
                created_new = (shm_id != -1);
        }
        
@@ -588,19 +643,23 @@ struct shmem_ops *sysv_shm_open(int size, int ronly)
                DEBUG(0,("ERROR shm_open : can't IPC_STAT\n"));
        }
 
-       /* set the permissions */
        if (!read_only) {
-               shm_ds.shm_perm.mode = IPC_PERMS;
-               shmctl(shm_id, IPC_SET, &shm_ds);
+               if (shm_ds.shm_perm.cuid != 0 || shm_ds.shm_perm.cgid != 0) {
+                       DEBUG(0,("ERROR: root did not create the shmem\n"));
+                       global_unlock();
+                       return NULL;
+               }
        }
 
+       shm_size = shm_ds.shm_segsz;
+
        other_processes = (shm_ds.shm_nattch > 1);
 
        if (!read_only && !other_processes) {
                memset((char *)shm_header_p, 0, shm_size);
                shm_initialize(shm_size);
-               shm_create_hash_table(lp_shmem_hash_size());
-               DEBUG(1,("Initialised IPC area of size %d\n", shm_size));
+               shm_create_hash_table(hash_size);
+               DEBUG(3,("Initialised IPC area of size %d\n", shm_size));
        } else if (!shm_validate_header(shm_size)) {
                /* existing file is corrupt, samba admin should remove
                    it by hand */