# module Puppet::Util::SUIDManager

### Public Class Methods

asuser(new_uid=nil, new_gid=nil) { || ... } click to toggle source

Runs block setting euid and egid if provided then restoring original ids. If running on Windows or without root, the block will be run with the current euid/egid.

    # File lib/puppet/util/suidmanager.rb, line 70
    def asuser(new_uid=nil, new_gid=nil)
      return yield if Puppet.features.microsoft_windows?
      return yield unless root?
      return yield unless new_uid or new_gid

      old_euid, old_egid = self.euid, self.egid
      begin
        change_privileges(new_uid, new_gid, false)

        yield
      ensure
        change_privileges(new_uid ? old_euid : nil, old_egid, false)
      end
    end

change_group(group, permanently=false) click to toggle source

Changes the egid of the process if `permanently` is not set, otherwise changes gid. This method will fail if used on Windows, or attempting to change to a different gid without root.

    # File lib/puppet/util/suidmanager.rb, line 107
    def change_group(group, permanently=false)
      gid = convert_xid(:gid, group)
      raise Puppet::Error, "No such group #{group}" unless gid

      if permanently
        Process::GID.change_privilege(gid)
      else
        Process.egid = gid
      end
    end

change_privileges(uid=nil, gid=nil, permanently=false) click to toggle source

If `permanently` is set, will permanently change the uid/gid of the process. If not, it will only set the euid/egid. If only uid is supplied, the primary group of the supplied gid will be used. If only gid is supplied, only gid will be changed. This method will fail if used on Windows.

    # File lib/puppet/util/suidmanager.rb, line 91
    def change_privileges(uid=nil, gid=nil, permanently=false)
      return unless uid or gid

      unless gid
        uid = convert_xid(:uid, uid)
        gid = Etc.getpwuid(uid).gid
      end

      change_group(gid, permanently)
      change_user(uid, permanently) if uid
    end

change_user(user, permanently=false) click to toggle source

As ::change_group, but operates on uids. If changing user permanently, supplementary groups will be set the to default groups for the new uid.

    # File lib/puppet/util/suidmanager.rb, line 121
    def change_user(user, permanently=false)
      uid = convert_xid(:uid, user)
      raise Puppet::Error, "No such user #{user}" unless uid

      if permanently
        # If changing uid, we must be root. So initgroups first here.
        initgroups(uid)

        Process::UID.change_privilege(uid)
      else
        # We must be root to initgroups, so initgroups before dropping euid if
        # we're root, otherwise elevate euid before initgroups.
        # change euid (to root) first.
        if Process.euid == 0
          initgroups(uid)
          Process.euid = uid
        else
          Process.euid = uid
          initgroups(uid)
        end
      end
    end

convert_xid(type, id) click to toggle source

Make sure the passed argument is a number.

    # File lib/puppet/util/suidmanager.rb, line 146
    def convert_xid(type, id)
      map = {:gid => :group, :uid => :user}
      raise ArgumentError, "Invalid id type #{type}" unless map.include?(type)
      ret = Puppet::Util.send(type, id)
      if ret == nil
        raise Puppet::Error, "Invalid #{map[type]}: #{id}"
      end
      ret
    end

groups=(grouplist) click to toggle source

    # File lib/puppet/util/suidmanager.rb, line 32
    def groups=(grouplist)
      begin
        return Process.groups = grouplist
      rescue Errno::EINVAL => e
        #We catch Errno::EINVAL as some operating systems (OS X in particular) can
        # cause troubles when using Process#groups= to change *this* user / process
        # list of supplementary groups membership.  This is done via Ruby's function
        # "static VALUE proc_setgroups(VALUE obj, VALUE ary)" which is effectively
        # a wrapper for "int setgroups(size_t size, const gid_t *list)" (part of SVr4
        # and 4.3BSD but not in POSIX.1-2001) that fails and sets errno to EINVAL.
        #
        # This does not appear to be a problem with Ruby but rather an issue on the
        # operating system side.  Therefore we catch the exception and look whether
        # we run under OS X or not -- if so, then we acknowledge the problem and
        # re-throw the exception otherwise.
        if osx_maj_ver and not osx_maj_ver.empty?
          return true
        else
          raise e
        end
      end
    end

initgroups(uid) click to toggle source

Initialize primary and supplemental groups to those of the target user. We take the UID and manually look up their details in the system database, including username and primary group. This method will fail on Windows, or if used without root to initgroups of another user.

    # File lib/puppet/util/suidmanager.rb, line 161
    def initgroups(uid)
      pwent = Etc.getpwuid(uid)
      Process.initgroups(pwent.name, pwent.gid)
    end

osx_maj_ver() click to toggle source

    # File lib/puppet/util/suidmanager.rb, line 19
    def osx_maj_ver
      return @osx_maj_ver unless @osx_maj_ver.nil?
      # 'kernel' is available without explicitly loading all facts
      if Facter.value('kernel') != 'Darwin'
        @osx_maj_ver = false
        return @osx_maj_ver
      end
      # But 'macosx_productversion_major' requires it.
      Facter.loadfacts
      @osx_maj_ver = Facter.value('macosx_productversion_major')
    end

root?() click to toggle source

    # File lib/puppet/util/suidmanager.rb, line 56
    def self.root?
      return Process.uid == 0 unless Puppet.features.microsoft_windows?

      require 'puppet/util/windows/user'
      Puppet::Util::Windows::User.admin?
    end

run_and_capture(command, new_uid=nil, new_gid=nil, options = {}) click to toggle source

Run a command and capture the output Parameters:

command

  the command to execute

new_uid

  (optional) a userid to run the command as

new_gid

  (optional) a groupid to run the command as

options

  (optional, defaults to {}) a hash of option key/value pairs; currently supported:

    :override_locale (defaults to true) a flag indicating whether or puppet should temporarily override the
      system locale for the duration of the command.  If true, the locale will be set to 'C' to ensure consistent
      output / formatting from the command, which makes it much easier to parse the output.  If false, the system
      locale will be respected.
    :custom_environment (default {}) -- a hash of key/value pairs to set as environment variables for the duration
      of the command

    # File lib/puppet/util/suidmanager.rb, line 180
    def run_and_capture(command, new_uid=nil, new_gid=nil, options = {})

      # specifying these here rather than in the method signature to allow callers to pass in a partial
      # set of overrides without affecting the default values for options that they don't pass in
      default_options = {
          :override_locale => true,
          :custom_environment => {},
      }

      options = default_options.merge(options)

      output = Puppet::Util::Execution.execute(command, :failonfail => false, :combine => true,
                                    :uid => new_uid, :gid => new_gid,
                                    :override_locale => options[:override_locale],
                                    :custom_environment => options[:custom_environment])
      [output, $CHILD_STATUS.dup]
    end