Module: Puppet::Util::SUIDManager

Extended by:
Forwardable
Includes:
Warnings
Defined in:
lib/puppet/util/suidmanager.rb

Class Method Summary collapse

Methods included from Warnings

clear_warnings, debug_once, maybe_log, notice_once, warnonce

Class Method Details

.asuser(new_uid = nil, new_gid = nil) ⇒ Object

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.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/puppet/util/suidmanager.rb', line 65

def asuser(new_uid=nil, new_gid=nil)
  return yield if Puppet::Util::Platform.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) ⇒ Object

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.

Raises:



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/puppet/util/suidmanager.rb', line 102

def change_group(group, permanently=false)
  gid = convert_xid(:gid, group)
  raise Puppet::Error, _("No such group %{group}") % { group: group } unless gid

  return if Process.egid == gid

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

.change_privileges(uid = nil, gid = nil, permanently = false) ⇒ Object

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.



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/puppet/util/suidmanager.rb', line 86

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) ⇒ Object

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

Raises:



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/puppet/util/suidmanager.rb', line 118

def change_user(user, permanently=false)
  uid = convert_xid(:uid, user)
  raise Puppet::Error, _("No such user %{user}") % { user: user } unless uid

  return if Process.euid == 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) ⇒ Object

Make sure the passed argument is a number.

Raises:

  • (ArgumentError)


145
146
147
148
149
150
151
152
153
154
# File 'lib/puppet/util/suidmanager.rb', line 145

def convert_xid(type, id)
  return id if id.kind_of? Integer
  map = {:gid => :group, :uid => :user}
  raise ArgumentError, _("Invalid id type %{type}") % { type: type } unless map.include?(type)
  ret = Puppet::Util.send(type, id)
  if ret == nil
    raise Puppet::Error, _("Invalid %{klass}: %{id}") % { klass: map[type], id: id }
  end
  ret
end

.groups=(grouplist) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/puppet/util/suidmanager.rb', line 25

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) ⇒ Object

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.



161
162
163
164
# File 'lib/puppet/util/suidmanager.rb', line 161

def initgroups(uid)
  pwent = Etc.getpwuid(uid)
  Process.initgroups(pwent.name, pwent.gid)
end

.osx_maj_verObject



19
20
21
22
# File 'lib/puppet/util/suidmanager.rb', line 19

def osx_maj_ver
  return @osx_maj_ver unless @osx_maj_ver.nil?
  @osx_maj_ver = Puppet.runtime[:facter].value('os.macosx.version.major') || false
end

.root?Boolean

Returns:

  • (Boolean)


49
50
51
52
53
54
55
56
# File 'lib/puppet/util/suidmanager.rb', line 49

def self.root?
  if Puppet::Util::Platform.windows?
    require_relative '../../puppet/util/windows/user'
    Puppet::Util::Windows::User.admin?
  else
    Process.uid == 0
  end
end