Blame view

libmailutils/base/userprivs.c 4.58 KB
Sergey Poznyakoff authored
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2
   Copyright (C) 2008, 2010 Free Software Foundation, Inc.
Sergey Poznyakoff authored
3 4 5 6 7 8 9 10 11 12 13 14

   GNU Mailutils is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   GNU Mailutils is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
15
   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
Sergey Poznyakoff authored
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <mailutils/assoc.h>
#include <mailutils/errno.h>
#include <mailutils/error.h>
#include <mailutils/errno.h>
#include <mailutils/nls.h>
#include <mailutils/list.h>
#include <mailutils/iterator.h>

/* Switch to the given UID/GID */
int
35
mu_set_user_privileges (uid_t uid, gid_t *gidv, size_t gidc)
Sergey Poznyakoff authored
36 37
{
  int rc = 0;
38
  gid_t gid;
Sergey Poznyakoff authored
39

40 41 42
  if (getuid ())
    return EACCES;
  
Sergey Poznyakoff authored
43 44 45
  if (uid == 0)
    return 0;

46 47
  /* Reset group permissions */
  if (gidv && gidc)
Sergey Poznyakoff authored
48
    {
49 50 51 52 53 54 55
      if (geteuid () == 0 && setgroups (gidc, gidv))
	{
	  mu_error(_("setgroups(1, %lu) failed: %s"),
		   (unsigned long) gidv[0], mu_strerror (errno));
	  return errno;
	}
      gid = gidv[0];
Sergey Poznyakoff authored
56
    }
57
  else
Sergey Poznyakoff authored
58
    {
59 60 61 62 63
      struct passwd *pwd = getpwuid (uid);
      if (pwd)
	gid = pwd->pw_gid;
      else
	gid = getegid ();
Sergey Poznyakoff authored
64
    }
65

Sergey Poznyakoff authored
66 67 68 69
  /* Switch to the user's gid. On some OSes the effective gid must
     be reset first */

#if defined(HAVE_SETEGID)
70 71 72 73 74 75
  if (setegid (gid) < 0)
    {
      rc = errno;
      mu_error (_("setegid(%lu) failed: %s"),
		(unsigned long) gid, mu_strerror (rc));
    }
Sergey Poznyakoff authored
76
#elif defined(HAVE_SETREGID)
77 78 79 80 81 82 83
  if (setregid (gid, gid) < 0)
    {
      rc = errno;
      mu_error (_("setregid(%lu,%lu) failed: %s"),
		(unsigned long) gid, (unsigned long) gid,
		mu_strerror (rc));
    }
Sergey Poznyakoff authored
84
#elif defined(HAVE_SETRESGID)
85 86 87 88 89 90 91 92 93
  if (setresgid (gid, gid, gid) < 0)
    {
      rc = errno;
      mu_error (_("setresgid(%lu,%lu,%lu) failed: %s"),
		(unsigned long) gid,
		(unsigned long) gid,
		(unsigned long) gid,
		mu_strerror (rc));
    }
Sergey Poznyakoff authored
94 95 96 97
#endif

  if (rc == 0 && gid != 0)
    {
98 99 100 101 102 103 104 105 106 107 108 109 110
      if (setgid (gid) < 0)
	{
	  rc = errno;
	  mu_error (_("setgid(%lu) failed: %s"),
		    (unsigned long) gid, mu_strerror (rc));
	}
      else if (getegid () != gid)
	{
	  rc = MU_ERR_FAILURE;
	  mu_error (_("setgid(%lu) failed: %s"),
		    (unsigned long) gid, mu_strerror (rc));
	}
      
Sergey Poznyakoff authored
111 112 113 114
      if (rc == 0 && getegid () != gid)
	{
	  mu_error (_("Cannot set effective gid to %lu"),
		    (unsigned long) gid);
115
	  rc = MU_ERR_FAILURE;
Sergey Poznyakoff authored
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	}
    }

  /* Now reset uid */
  if (rc == 0 && uid != 0)
    {
      uid_t euid;

      if (setuid (uid) || geteuid () != uid
	  || (getuid () != uid && (geteuid () == 0 || getuid () == 0)))
	{
#if defined(HAVE_SETREUID)
	  if (geteuid () != uid)
	    {
	      if (setreuid (uid, -1) < 0)
		{
132
		  rc = errno;
Sergey Poznyakoff authored
133 134
		  mu_error (_("setreuid(%lu,-1) failed: %s"),
			    (unsigned long) uid,
135
			    mu_strerror (rc));
Sergey Poznyakoff authored
136 137 138
		}
	      if (setuid (uid) < 0)
		{
139
		  rc = errno;
Sergey Poznyakoff authored
140
		  mu_error (_("second setuid(%lu) failed: %s"),
141
			    (unsigned long) uid, mu_strerror (rc));
Sergey Poznyakoff authored
142 143 144 145
		}
	    } else
#endif
	        {
146
		  rc = errno;
Sergey Poznyakoff authored
147 148
		  mu_error (_("setuid(%lu) failed: %s"),
			    (unsigned long) uid,
149
			    mu_strerror (rc));
Sergey Poznyakoff authored
150 151 152 153 154 155 156
		}
	}
	
      euid = geteuid ();
      if (uid != 0 && setuid (0) == 0)
	{
	  mu_error (_("seteuid(0) succeeded when it should not"));
157
	  rc = MU_ERR_FAILURE;
Sergey Poznyakoff authored
158 159 160 161
	}
      else if (uid != euid && setuid (euid) == 0)
	{
	  mu_error (_("Cannot drop non-root setuid privileges"));
162
	  rc = MU_ERR_FAILURE;
Sergey Poznyakoff authored
163 164 165 166 167
	}
    }
  return rc;
}

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
int
mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
{
  int rc = 0;
  gid_t *emptygidset;
  size_t size = 1, j = 1;
  mu_iterator_t itr;

  if (uid == 0)
    return 0;

  /* Create a list of supplementary groups */
  mu_list_count (retain_groups, &size);
  size++;
  emptygidset = malloc (size * sizeof emptygidset[0]);
  if (!emptygidset)
    return ENOMEM;
  emptygidset[0] = gid ? gid : getegid ();

  if (mu_list_get_iterator (retain_groups, &itr) == 0)
    {
      for (mu_iterator_first (itr);
	   !mu_iterator_is_done (itr); mu_iterator_next (itr)) 
	mu_iterator_current (itr,
			     (void **)(emptygidset + j++));
      mu_iterator_destroy (&itr);
    }
  rc = mu_set_user_privileges (uid, emptygidset, j);
  free (emptygidset);
  return rc;
}
Sergey Poznyakoff authored
199