private/privdrop.ss
(module privdrop mzscheme
  (require "ffi-utils.ss"
           (all-except (lib "contract.ss") ->)
           (rename (lib "contract.ss") => ->)
           (lib "foreign.ss")) (unsafe!)
  
  (provide privdrop)
  
  ;; use utf-8 strings
  (default-_string-type _string*/utf-8)

  (define _uid_t _uint32)
  (define _gid_t _uint32)
  (define _time_t _uint32)
  
  (define _cbool
    (make-ctype _int32
                #f
                (lambda (val)
                  (not (= val -1)))))
  
  (define-cstruct _passwd
    ([pw_name _string]
     [pw_passwd _string]
     [pw_uid _uid_t]
     [pw_gid _gid_t]
     [pw_change _time_t]
     [pw_class _string]
     [pw_gecos _string]
     [pw_dir _string]
     [pw_shell _string]
     [pw_expire _time_t]
     [pw_fields _int]))
  
  (define libc (ffi-lib #f))
  
  (define-syntax unix-only-ffi
    (syntax-rules ()
      ([_ promise function lib type]
       (define promise
         (delay
           (get-ffi-obj function
                        lib
                        type))))))
  
  
  ;; Getpwnam
  (unix-only-ffi getpwnam-promise
                 "getpwnam"
                 libc
                 (_fun _string -> (_ptr o _passwd)))
  
  ;; setgid
  (unix-only-ffi setgid-promise
                 "setgid"
                 libc
                 (_fun _gid_t -> _cbool))
  
  ;; setuid
  (unix-only-ffi setuid-promise
                 "setuid"
                 libc
                 (_fun _uid_t -> _cbool))
  
  
  ;; Drop the user
  (define (privdrop user)
    (case (system-type)
      [(unix)
       (let ([pw ((force getpwnam-promise) user)])
         (and pw 
              ((force setgid-promise) (passwd-pw_gid pw))
              ((force setuid-promise) (passwd-pw_uid pw))))]
      [(windows) #f]))
  
  )