1 /* |
|
2 * bsdjail.c - JParallel extension for FreeBSD jail(2) functions |
|
3 * $Id$ |
|
4 * |
|
5 * Authors: |
|
6 * * Michael Granger <ged@FaerieMUD.org> |
|
7 * * Mahlon E. Smith <mahlon@martini.nu> |
|
8 * |
|
9 * Copyright (c) 2008, Michael Granger and Mahlon Smith |
|
10 * All rights reserved. |
|
11 * |
|
12 * Redistribution and use in source and binary forms, with or without |
|
13 * modification, are permitted provided that the following conditions are met: |
|
14 * |
|
15 * * Redistributions of source code must retain the above copyright notice, |
|
16 * this list of conditions and the following disclaimer. |
|
17 * |
|
18 * * Redistributions in binary form must reproduce the above copyright notice, |
|
19 * this list of conditions and the following disclaimer in the documentation |
|
20 * and/or other materials provided with the distribution. |
|
21 * |
|
22 * * Neither the name of the author/s, nor the names of the project's |
|
23 * contributors may be used to endorse or promote products derived from this |
|
24 * software without specific prior written permission. |
|
25 * |
|
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
27 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
29 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
|
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
36 * |
|
37 */ |
|
38 |
|
39 #include "bsdjail.h" |
|
40 |
|
41 VALUE rbjail_mBSD; |
|
42 VALUE rbjail_cBSDJail; |
|
43 |
|
44 |
|
45 /* |
|
46 * Debug logging function |
|
47 */ |
|
48 void |
|
49 #ifdef HAVE_STDARG_PROTOTYPES |
|
50 rbjail_debug(const char *fmt, ...) |
|
51 #else |
|
52 rbjail_debug( const char *fmt, va_dcl ) |
|
53 #endif |
|
54 { |
|
55 char buf[BUFSIZ], buf2[BUFSIZ]; |
|
56 va_list args; |
|
57 |
|
58 if ( !RTEST(ruby_debug) ) return; |
|
59 |
|
60 snprintf( buf, BUFSIZ, "Debug>>> %s", fmt ); |
|
61 |
|
62 va_init_list( args, fmt ); |
|
63 vsnprintf( buf2, BUFSIZ, buf, args ); |
|
64 fputs( buf2, stderr ); |
|
65 fputs( "\n", stderr ); |
|
66 fflush( stderr ); |
|
67 va_end( args ); |
|
68 } |
|
69 |
|
70 |
|
71 /* |
|
72 struct jail { |
|
73 u_int32_t version; |
|
74 char *path; |
|
75 char *hostname; |
|
76 u_int32_t ip_number; |
|
77 }; |
|
78 */ |
|
79 |
|
80 /* |
|
81 * Allocation function |
|
82 */ |
|
83 static jail * |
|
84 rbjail_jail_alloc() { |
|
85 jail *ptr = ALLOC( jail ); |
|
86 |
|
87 ptr->version = 0; |
|
88 ptr->path = NULL; |
|
89 ptr->hostname = NULL; |
|
90 ptr->ip_number = 0; |
|
91 |
|
92 debugMsg(( "Initialized a jail pointer <%p>", ptr )); |
|
93 return ptr; |
|
94 } |
|
95 |
|
96 |
|
97 /* |
|
98 * GC Free function |
|
99 */ |
|
100 static void |
|
101 rbjail_gc_free( jail *ptr ) { |
|
102 if ( ptr ) { |
|
103 if ( ptr->path ) xfree( ptr->path ); |
|
104 if ( ptr->hostname ) xfree( ptr->hostname ); |
|
105 |
|
106 ptr->path = NULL; |
|
107 ptr->hostname = NULL; |
|
108 |
|
109 xfree( ptr ); |
|
110 } |
|
111 |
|
112 else { |
|
113 debugMsg(( "Not freeing an uninitialized jail" )); |
|
114 } |
|
115 } |
|
116 |
|
117 |
|
118 /* |
|
119 * Object validity checker. Returns the data pointer. |
|
120 */ |
|
121 static jail * |
|
122 rbjail_check_jail( VALUE self ) { |
|
123 debugMsg(( "Checking a BSD::Jail object (%d).", self )); |
|
124 Check_Type( self, T_DATA ); |
|
125 |
|
126 if ( !rb_obj_is_kind_of(self, rbjail_cBSDJail) ) { |
|
127 rb_raise( rb_eTypeError, "wrong argument type %s (expected BSD::Jail)", |
|
128 rb_class2name(CLASS_OF( self )) ); |
|
129 } |
|
130 |
|
131 return DATA_PTR( self ); |
|
132 } |
|
133 |
|
134 |
|
135 /* |
|
136 * Fetch the data pointer and check it for sanity. |
|
137 */ |
|
138 static jail * |
|
139 rbjail_get_jail( VALUE self ) { |
|
140 jail *ptr = check_sentence( self ); |
|
141 |
|
142 debugMsg(( "Fetching a Jail (%p).", ptr )); |
|
143 if ( !ptr ) |
|
144 rb_raise( rb_eRuntimeError, "uninitialized Sentence" ); |
|
145 |
|
146 return ptr; |
|
147 } |
|
148 |
|
149 |
|
150 /* -------------------------------------------------------------- |
|
151 * Jail utility functions |
|
152 * -------------------------------------------------------------- */ |
|
153 |
|
154 /* |
|
155 * Try to jail_attach() to the specified +jid+, raising an exception if it fails. |
|
156 */ |
|
157 static void |
|
158 rbjail_do_jail_attach( int jid ) |
|
159 { |
|
160 if ( jail_attach(jid) == -1 ) |
|
161 rb_sys_fail( "jail_attach" ); |
|
162 } |
|
163 |
|
164 |
|
165 /* |
|
166 * Fork + Block function for rbjail_attach(). Mostly stolen from Ruby's process.c. |
|
167 */ |
|
168 static VALUE |
|
169 rbjail_attach_block( int jid ) |
|
170 { |
|
171 int pid; |
|
172 |
|
173 rb_secure(2); |
|
174 |
|
175 fflush( stdout ); |
|
176 fflush( stderr ); |
|
177 |
|
178 switch ( pid = fork() ) { |
|
179 case 0: |
|
180 rb_thread_atfork(); |
|
181 if ( rb_block_given_p() ) { |
|
182 int status; |
|
183 |
|
184 rbjail_do_jail_attach( jid ); |
|
185 rb_protect( rb_yield, Qundef, &status ); |
|
186 ruby_stop( status ); |
|
187 } |
|
188 return Qnil; |
|
189 |
|
190 case -1: |
|
191 rb_sys_fail( "fork(2)" ); |
|
192 return Qnil; |
|
193 |
|
194 default: |
|
195 return INT2FIX( pid ); |
|
196 } |
|
197 } |
|
198 |
|
199 |
|
200 /* -------------------------------------------------------------- |
|
201 * Class methods |
|
202 * -------------------------------------------------------------- */ |
|
203 |
|
204 /* |
|
205 * call-seq: |
|
206 * BSD::Jail.allocate -> bsdjail |
|
207 * |
|
208 * Allocate a new BSD::Jail object. |
|
209 * |
|
210 */ |
|
211 static VALUE |
|
212 rbjail_jail_s_allocate( VALUE klass ) { |
|
213 return Data_Wrap_Struct( klass, 0, rbjail_gc_free, 0 ); |
|
214 } |
|
215 |
|
216 |
|
217 /* |
|
218 * call-seq: |
|
219 * BSD::Jail.list -> array |
|
220 * |
|
221 * Return an Array of all the running jails on the host. |
|
222 * |
|
223 */ |
|
224 static VALUE |
|
225 rbjail_jail_s_list( VALUE klass ) { |
|
226 VALUE rval = rb_ary_new(); |
|
227 struct xprison *xp; |
|
228 struct in_addr in; |
|
229 size_t i, len; |
|
230 |
|
231 if ( sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1 ) |
|
232 rb_sys_fail( "sysctlbyname(): jail.list" ); |
|
233 |
|
234 xp = ALLOCA_N( xprison, 1 ); |
|
235 |
|
236 if ( sysctlbyname("jail.list", xp, &len, NULL, 0) == -1 ) { |
|
237 rb_sys_fail( "sysctlbyname(): jail.list" ); |
|
238 } |
|
239 |
|
240 if ( len < sizeof(*xp) || len % sizeof(*xp) || |
|
241 xp->pr_version != KINFO_PRISON_VERSION ) |
|
242 rb_fatal( "Kernel and userland out of sync" ); |
|
243 |
|
244 len /= sizeof( *xp ); |
|
245 for ( i = 0; i < len; i++ ) { |
|
246 VALUE jail, args[3]; |
|
247 |
|
248 /* Hostname */ |
|
249 args[0] = rb_str_new2( xp->pr_host ); |
|
250 |
|
251 /* IP */ |
|
252 in.s_addr = ntohl( xp->pr_ip ); |
|
253 args[1] = rb_str_new2( inet_ntoa(in) ); |
|
254 |
|
255 /* Path */ |
|
256 args[2] = rb_str_new2( xp->pr_path ); |
|
257 |
|
258 jail = rb_class_new_instance( 3, args, klass ); |
|
259 rb_ary_push( rval, jail ); |
|
260 |
|
261 xp++; |
|
262 } |
|
263 |
|
264 return rval; |
|
265 } |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 /* -------------------------------------------------------------- |
|
271 * Instance methods |
|
272 * -------------------------------------------------------------- */ |
|
273 |
|
274 /* |
|
275 * call-seq: |
|
276 * BSD::Jail.new( hostname, ip, path ) -> new_jail |
|
277 * |
|
278 * Create a new jail for the given +hostname+, +ip+, and +path+. |
|
279 */ |
|
280 static VALUE |
|
281 rbjail_jail_initialize( VALUE self, VALUE hostname, VALUE ip, VALUE path ) { |
|
282 if ( !rbjail_check_jail(self) ) { |
|
283 struct jail *ptr = rbjail_jail_alloc(); |
|
284 char *pathstr = NULL, *hostnamestr = NULL; |
|
285 |
|
286 Check_Type( hostname, T_STRING ); |
|
287 Check_Type( ip, T_STRING ); |
|
288 |
|
289 pathstr = ALLOC_N( char, RSTRING(path)->len ); |
|
290 strncpy( pathstr, RSTRING(path)->ptr, RSTRING(path)->len ); |
|
291 |
|
292 hostnamestr = ALLOC_N( char, RSTRING(hostname)->len ); |
|
293 strncpy( hostnamestr, RSTRING(hostname)->ptr, RSTRING(hostname)->len ); |
|
294 |
|
295 /* |
|
296 struct jail { |
|
297 u_int32_t version; |
|
298 char *path; |
|
299 char *hostname; |
|
300 u_int32_t ip_number; |
|
301 }; |
|
302 */ |
|
303 ptr->version = 0; /* Per the manpage's recommendation */ |
|
304 ptr->ip_number = inet_addr( StringValuePtr(ip) ); |
|
305 ptr->path = path; |
|
306 ptr->hostname = hostname; |
|
307 } |
|
308 } |
|
309 |
|
310 |
|
311 /* |
|
312 * call-seq: |
|
313 * jail.attach -> true or false |
|
314 * jail.attach { block } -> pid |
|
315 * |
|
316 * Attach to the given jail. In the non-block form, attach the current process to the |
|
317 * jail and return +true+ if it succeeds. This is a one-way operation, and requires root |
|
318 * privileges. |
|
319 * |
|
320 * In the block form, the process will be forked, and the block will be attached to the jail and |
|
321 * run by the child. The parent process will receive the process ID of the child. |
|
322 */ |
|
323 static VALUE |
|
324 rbjail_attach( int argc, VALUE *argv, VALUE self ) { |
|
325 VALUE jidnum, rval; |
|
326 int jid; |
|
327 |
|
328 rb_scan_args( argc, argv, "1", &jidnum ); |
|
329 jid = NUM2INT( jidnum ); |
|
330 |
|
331 if ( rb_block_given_p() ) { |
|
332 rval = rbjail_attach_block( jid ); |
|
333 } |
|
334 |
|
335 else { |
|
336 rbjail_do_jail_attach( jid ); |
|
337 rval = Qtrue; |
|
338 } |
|
339 |
|
340 return rval; |
|
341 } |
|
342 |
|
343 |
|
344 /* |
|
345 * I can't remember how the hell you document a class, but that will go here. |
|
346 */ |
|
347 void |
|
348 Init_bsdjail( void ) { |
|
349 rbjail_mBSD = rb_define_module( "BSD" ); |
|
350 rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail" ); |
|
351 |
|
352 /* Class methods */ |
|
353 rb_define_alloc_function( rbjail_cBSDJail, rbjail_jail_s_allocate ); |
|
354 rb_define_singleton_method( rbjail_cBSDJail, "jail", rbjail_jail_s_jail, 0 ); |
|
355 rb_define_singleton_method( rbjail_cBSDJail, "list", rbjail_jail_s_list, 0 ); |
|
356 |
|
357 /* Instance methods */ |
|
358 rb_define_method( rbjail_cBSDJail, "initialize", rbjail_jail_initialize, 3 ); |
|
359 rb_define_method( rbjail_cBSDJail, "attach", rbjail_jail_attach, -1 ); |
|
360 rb_define_method( rbjail_cBSDJail, "hostname", rbjail_jail_hostname, 0 ); |
|
361 rb_define_method( rbjail_cBSDJail, "path", rbjail_jail_path, 0 ); |
|
362 rb_define_method( rbjail_cBSDJail, "ip", rbjail_jail_ip, 0 ); |
|
363 rb_define_method( rbjail_cBSDJail, "jid", rbjail_jail_jid, 0 ); |
|
364 |
|
365 } |
|
366 |
|