File: mod_linuxprivs.c

package info (click to toggle)
proftpd 1.2.0pre1-2
  • links: PTS
  • area: main
  • in suites: hamm
  • size: 1,468 kB
  • ctags: 2,106
  • sloc: ansic: 19,206; sh: 1,640; makefile: 480; perl: 235
file content (206 lines) | stat: -rw-r--r-- 5,970 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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
199
200
201
202
203
204
205
206
/*
 * ProFTPD - FTP server daemon
 * Copyright (c) 1997, 1998 Public Flood Software
 *  
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * Optional module for Linux 2.1.104 and > ONLY.  Uses the new "capabilities"
 * in 2.1 kernels which allow root to perform fine grained control
 * over system calls which may be performed (essential making root
 * "not-root" in terms of syscalls).  Excellent for security.  See
 * README.linux-privs.  In order to use this module you'll _need_ a
 * a 2.1 kernel.  A psuedo-port of libcap is included with proftpd
 * in the contrib/libcap directory.  From the top-level directory
 * run ./configure --with-modules=mod_linuxprivs
 * This will automatically build libcap and link it to proftpd.
 *
 * This module effectively _completely_ gives up root, except for
 * the bare minimum functionality that is required, after user
 * authentication.  VERY highly recommended for security-consious admins.
 *
 * NOTE: Linux kernels 2.1 are _development_ kernels, and are likely
 * to change over time.  As this happens, we'll try to keep this module
 * up to date.  Additionally, libcap is a tad buggy and not completely
 * standardized.  This should be solved be the time Linux 2.2 is released.
 * In the interim, we've had to "work-around" a few bugs in libcap, which
 * is why a separate static library is included.
 *
 * -- DO NOT MODIFY THE TWO LINES BELOW --
 * $Libraries: -Lcontrib/libcap -lcap$
 * $Directories: contrib/libcap$
 * $Id: mod_linuxprivs.c,v 1.1.1.1 1998/10/18 02:24:41 flood Exp $
 */

#include "conf.h"
#include "privs.h"

#include "../contrib/libcap/capability.h"

static cap_t capabilities = 0;
static int use_capabilities = 1;

/* log current capabilities */
static void lp_debug()
{
  cap_t caps;
  char *res;
  ssize_t len;

  caps = cap_get_proc();
  if(!caps) {
    log_pri(LOG_ERR,"module linuxprivs: cap_get_proc failed: %s",
            strerror(errno));
    return;
  }

  res = cap_to_text(caps,&len);
  if(!res) {
    log_pri(LOG_ERR,"module linuxprivs: cap_to_text failed: %s",
            strerror(errno));
    cap_free(&caps);
    return;
  }

  log_debug(DEBUG1,"module linuxprivs: capabilities '%s'", res);
  cap_free(&caps);
  free(res);
}

/* create a new capability structure */
static int lp_init_cap()
{
  if( !(capabilities = cap_init()) ) {
    log_pri(LOG_ERR,"module linuxprivs: cap_init failed: %s",
            strerror(errno));
    return -1;
  }
  return 0;
}

/* free the capability structure */
static void lp_free_cap()
{
  cap_free(&capabilities);
}

/* add a capability to a given set */
static int lp_add_cap(cap_value_t cap, cap_flag_t set)
{
  if(cap_set_flag(capabilities, set, 1, &cap, CAP_SET) == -1) {
    log_pri(LOG_ERR,"module linuxprivs: cap_set_flag failed: %s",
                    strerror(errno));
    return -1;
  }

  return 0;
}

/* send the capabilities to the kernel */
static int lp_set_cap()
{
  if(cap_set_proc(capabilities) == -1) {
    log_pri(LOG_ERR,"module linuxprivs: cap_set_proc failed: %s",
                    strerror(errno));
    return -1;
  }

  return 0;
}

/* The post cmd handler for "PASS" is only called after PASS has
 * successfully completed, which means authentication is successful,
 * so we can "tweak" our root access down to almost nothing.
 */

MODRET lowerprivs(cmd_rec *cmd)
{
  int ret;

  if(!use_capabilities)
    return DECLINED(cmd);

  block_signals();
  PRIVS_ROOT

  /* The only capability we need is CAP_NET_BIND_SERVICE (bind
   * ports < 1024).  Everything else can be discarded.  We set this
   * in CAP_PERMITTED set only, as when we switch away from root
   * we lose CAP_EFFECTIVE anyhow, and must reset it.
   */

  ret = lp_init_cap();
  if(ret != -1)
    ret = lp_add_cap(CAP_NET_BIND_SERVICE,CAP_PERMITTED);
  if(ret != -1)
    ret = lp_set_cap();

  PRIVS_RELINQUISH
  unblock_signals();

  /* now our ownly capabilities consist of CAP_NET_BIND_SERVICE,
   * however in order to actually be able to bind to low-numbered
   * ports, we need the capability to be in the effective set.
   */

  if(ret != -1)
    ret = lp_add_cap(CAP_NET_BIND_SERVICE,CAP_EFFECTIVE);
  if(ret != -1)
    ret = lp_set_cap();
  if(capabilities)
    lp_free_cap();

  if(ret != -1) {
    /* That's it!  Disable all further id switching */
    session.disable_id_switching = TRUE;
    lp_debug();
  } else
    log_pri(LOG_NOTICE,"attempt to configure capabilities failed, reverting to normal operation");

  return DECLINED(cmd);
}

static int linuxprivs_init()
{
  /* Attempt to determine if we are running on a kernel that supports
   * linuxprivs, this allows binary distributions to include the module
   * even if it may not work.
   */

  if(!cap_get_proc() && errno == ENOSYS) {
#if 0
    log_debug(DEBUG1,"module linuxprivs: kernel does not support capabilities, disabling");
#endif
    use_capabilities = 0;
  }

  return 0;
}  

cmdtable linuxprivs_commands[] = {
  { POST_CMD,	C_PASS,	G_NONE,	lowerprivs,	TRUE, FALSE },
  { 0, NULL }
};

module linuxprivs_module = {
  NULL, NULL,					/* Always NULL */
  0x20,						/* API Version */
  "linuxprivs",					/* Module name */
  NULL,						/* No directive table */
  linuxprivs_commands,				/* Command handler table */
  NULL,						/* No auth table */
  linuxprivs_init,NULL				/* No child init */
};