1 <?php
2 /*
3 * This code is part of GOsa (http://www.gosa-project.org)
4 * Copyright (C) 2003-2008 GONICUS GmbH
5 *
6 * ID: $$Id: class_sudo.inc 9975 2008-03-25 14:09:30Z hickert $$
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
24 /*! \brief Sudo option class.
25 Allows setting flags/options for a sudo role.
26 */
27 class sudoOption extends plugin
28 {
29 /* Group attributes */
30 protected $sudoOption = array();
31 public $attributes = array("sudoOption");
32 private $options = array();
33 public $ignore_account = TRUE;
35 /*! \brief Initializes this class
36 @param Object $config The GOsa configuration object.
37 @param String $dn The object dn.
38 */
39 function sudoOption(&$config, $dn= NULL)
40 {
41 plugin::plugin ($config, $dn);
43 /****
44 Create a list of known options
45 ****/
46 $options = array();
47 $option['long_otp_prompt']= array('NAME' =>'long_otp_prompt' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
48 $option['ignore_dot']= array('NAME' =>'ignore_dot' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
49 $option['mail_always']= array('NAME' =>'mail_always' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
50 $option['mail_badpass']= array('NAME' =>'mail_badpass' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
51 $option['mail_no_user']= array('NAME' =>'mail_no_user' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
52 $option['mail_no_host']= array('NAME' =>'mail_no_host' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
53 $option['mail_no_perms']= array('NAME' =>'mail_no_perms' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
54 $option['tty_tickets']= array('NAME' =>'tty_tickets' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
55 $option['authenticate']= array('NAME' =>'authenticate' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
56 $option['root_sudo']= array('NAME' =>'root_sudo' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
57 $option['log_host']= array('NAME' =>'log_host' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
58 $option['log_year']= array('NAME' =>'log_year' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
59 $option['shell_noargs']= array('NAME' =>'shell_noargs' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
60 $option['set_home']= array('NAME' =>'set_home' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
61 $option['always_set_home']= array('NAME' =>'always_set_home' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
62 $option['path_info']= array('NAME' =>'path_info' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
63 $option['preserve_groups']= array('NAME' =>'preserve_groups' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
64 $option['fqdn']= array('NAME' =>'fqdn' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
65 $option['insults']= array('NAME' =>'insults' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
66 $option['requiretty']= array('NAME' =>'requiretty' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
67 $option['env_editor']= array('NAME' =>'env_editor' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
68 $option['rootpw']= array('NAME' =>'rootpw' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
69 $option['runaspw']= array('NAME' =>'runaspw' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
70 $option['targetpw']= array('NAME' =>'targetpw' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
71 $option['set_logname']= array('NAME' =>'set_logname' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
72 $option['stay_setuid']= array('NAME' =>'stay_setuid' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
73 $option['env_reset']= array('NAME' =>'env_reset' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'TRUE');
74 $option['use_loginclass']= array('NAME' =>'use_loginclass' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
75 $option['noexec']= array('NAME' =>'noexec' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
76 $option['ignore_local_sudoers']= array('NAME' =>'ignore_local_sudoers' , 'TYPE' => 'BOOLEAN' , 'DEFAULT' => 'FALSE');
77 $option['passwd_tries']= array('NAME' =>'passwd_tries' , 'TYPE' => 'INTEGER' , 'DEFAULT' => 3);
78 $option['loglinelen']= array('NAME' =>'loglinelen' , 'TYPE' => 'BOOL_INTEGER' , 'DEFAULT' => 80);
79 $option['timestamp_timeout']= array('NAME' =>'timestamp_timeout' , 'TYPE' => 'BOOL_INTEGER' , 'DEFAULT' => 0);
80 $option['passwd_timeout']= array('NAME' =>'passwd_timeout' , 'TYPE' => 'BOOL_INTEGER' , 'DEFAULT' => 15);
81 $option['umask']= array('NAME' =>'umask' , 'TYPE' => 'BOOL_INTEGER' , 'DEFAULT' => "0022");
82 $option['mailsub']= array('NAME' =>'mailsub' , 'TYPE' => 'STRING' , 'DEFAULT' => '*** SECURITY information for %h ***');
83 $option['badpass_message']= array('NAME' =>'badpass_message' , 'TYPE' => 'STRING' , 'DEFAULT' => 'Sorry, try again');
84 $option['timestampdir']= array('NAME' =>'timestampdir' , 'TYPE' => 'STRING' , 'DEFAULT' => '/var/run/sudo');
85 $option['timestampowner']= array('NAME' =>'timestampowner' , 'TYPE' => 'STRING' , 'DEFAULT' => 'root');
86 $option['passprompt']= array('NAME' =>'passprompt' , 'TYPE' => 'STRING' , 'DEFAULT' => '[sudo] password for %p: ');
87 $option['runas_default']= array('NAME' =>'runas_default' , 'TYPE' => 'STRING' , 'DEFAULT' => 'root');
88 $option['syslog_goodpri']= array('NAME' =>'syslog_goodpri' , 'TYPE' => 'STRING' , 'DEFAULT' => 'notice');
89 $option['syslog_badpri']= array('NAME' =>'syslog_badpri' , 'TYPE' => 'STRING' , 'DEFAULT' => 'alert');
90 $option['editor']= array('NAME' =>'editor' , 'TYPE' => 'STRING' , 'DEFAULT' => '/usr/bin/vi');
91 $option['noexec_file']= array('NAME' =>'noexec_file' , 'TYPE' => 'STRING' , 'DEFAULT' => '/usr/lib/sudo/sudo_noexec.so');
92 $option['lecture']= array('NAME' =>'lecture' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => 'once');
93 $option['lecture_file']= array('NAME' =>'lecture_file' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => '');
94 $option['logfile']= array('NAME' =>'logfile' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => 'syslog');
95 $option['syslog']= array('NAME' =>'syslog' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => 'authpriv');
96 $option['mailerpath']= array('NAME' =>'mailerpath' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => '');
97 $option['mailerflags']= array('NAME' =>'mailerflags' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => '-t');
98 $option['mailto']= array('NAME' =>'mailto' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => 'root');
99 $option['exempt_group']= array('NAME' =>'exempt_group' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => 'root');
100 $option['verifypw']= array('NAME' =>'verifypw' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => 'all');
101 $option['listpw']= array('NAME' =>'listpw' , 'TYPE' => 'STRING_BOOL' , 'DEFAULT' => 'any');
102 $option['env_check']= array('NAME' =>'env_check' , 'TYPE' => 'LISTS' , 'DEFAULT' => '');
103 $option['env_delete']= array('NAME' =>'env_delete' , 'TYPE' => 'LISTS' , 'DEFAULT' => '');
104 $option['env_keep']= array('NAME' =>'env_keep' , 'TYPE' => 'LISTS' , 'DEFAULT' => '');
105 ksort($option);
106 $this->options = $option;
108 $this->load_options();
109 }
112 private function load_options()
113 {
115 /****
116 Parse given sudoOption attributes
117 ****/
118 $this->sudoOption = array();
119 if(isset($this->attrs['sudoOption'])){
120 for($i = 0 ; $i < $this->attrs['sudoOption']['count']; $i++){
122 /****
123 Detect attribute name/value/negation
124 ****/
125 $opt = $this->attrs['sudoOption'][$i];
127 /* Get negation */
128 $negation = FALSE;
129 if(preg_match("/^!/",$opt)){
130 $negation = TRUE;
131 $opt = preg_replace("/^!/","",$opt);
132 }
134 /* Get value / name*/
135 $value = "";
136 if(preg_match("/=/",$opt)){
137 $value = preg_replace("/^[^=]*+=/","",$opt);
138 $opt = preg_replace("/=.*$/","",$opt);
139 }
141 /* Special chars are escaped, remove escape char now.
142 \\ => \
143 \: => :
144 \, => ,
145 \= => =
146 */
147 $value = $this->unescape_command($value);
149 /* Check if the given value is part of our options list.
150 If it is not, add it as type STRING and display a warning.
151 */
152 if(!isset($this->options[$opt])){
153 $this->options[$opt]=array('NAME'=>$opt,'TYPE'=>'STRING','DEFAULT' => '');
154 msg_dialog::display(_("Unknown option"),
155 sprintf(_("The Sudo option '%s' is invalid!"),
156 $opt),WARNING_DIALOG);
157 }
159 /* Create internal sudoOption object */
160 $option = array();
161 $option['NAME'] = $opt;
162 $option['VALUE'] = $value;
163 $option['NEGATE'] = $negation;
165 /* Special handling for mixed flag types.
166 Some attributes like (BOOL_INTEGER) can be TRUE/FALSE and INTEGER.
167 This means, if the value is empty it is BOOL and $negation defines its boolean value.
168 */
169 if(in_array_strict($this->options[$opt]['TYPE'],array("BOOL_INTEGER","STRING_BOOL"))){
170 if(empty($value)){
171 $option['NEGATE'] = FALSE;
172 if($negation){
173 $option['VALUE'] = "FALSE";
174 }else{
175 $option['VALUE'] = "TRUE";
176 }
177 }
178 }
180 /* Special handling for BOOLEAN values */
181 if(in_array_strict($this->options[$opt]['TYPE'],array("BOOLEAN"))){
182 $option['NEGATE'] = FALSE;
183 if($negation){
184 $option['VALUE'] = "FALSE";
185 }else{
186 $option['VALUE'] = "TRUE";
187 }
188 }
190 /* Append values */
191 $this->sudoOption[$opt][] = $option;
192 }
193 }
194 }
198 /*! \brief Create HTML output for this plugin
199 @return String HTML output for this plugin.
200 */
201 function execute()
202 {
203 /* Call parent execute */
204 plugin::execute();
206 /*****
207 Handle Posts
208 *****/
209 if($this->acl_is_writeable("")){
211 foreach($_POST as $name => $value){
213 $value = get_post($name);
215 if(preg_match("/^negOption_/",$name)){
217 $opt = preg_replace("/^negOption_(.*)_[0-9]*$/","\\1", $name);
218 $id = preg_replace("/^negOption_.*_([0-9])*$/","\\1", $name);
220 if(isset($this->sudoOption[$opt][$id])){
221 $val = $this->sudoOption[$opt][$id]["VALUE"];
223 /*****
224 Negate STRING_BOOL && BOOL_INTEGER
225 *****/
226 if(in_array_strict($this->options[$opt]['TYPE'],array('STRING_BOOL','BOOL_INTEGER'))){
227 if(in_array_strict($val, array("TRUE","FALSE"))){
228 if($val == "TRUE"){
229 $this->sudoOption[$opt][$id]["VALUE"] = "FALSE";
230 }else{
231 $this->sudoOption[$opt][$id]["VALUE"] = "TRUE";
232 }
233 }else{
234 $this->sudoOption[$opt][$id]['NEGATE'] = !$this->sudoOption[$opt][$id]['NEGATE'];
235 }
236 }
238 /*****
239 Negate STRING / INTEGER
240 *****/
241 if(in_array_strict($this->options[$opt]['TYPE'],array('STRING','INTEGER','LISTS'))){
242 $this->sudoOption[$opt][$id]['NEGATE'] = !$this->sudoOption[$opt][$id]['NEGATE'];
243 }
245 /*****
246 Negate BOOLEAN
247 *****/
248 if(in_array_strict($this->options[$opt]['TYPE'],array('BOOLEAN'))){
249 if($val == "TRUE"){
250 $this->sudoOption[$opt][$id]["VALUE"] = "FALSE";
251 }else{
252 $this->sudoOption[$opt][$id]["VALUE"] = "TRUE";
253 }
254 }
255 }
256 break;
257 }
259 /*****
260 Remove options
261 *****/
262 if(preg_match("/^delOption/",$name)){
263 $opt = preg_replace("/^delOption_/","",$name);
264 $opt = preg_replace("/_[^_]*$/","",$opt);
265 $id = preg_replace("/^.*_([0-9])*$/","\\1",$opt);
266 $opt = preg_replace("/_[0-9]*$/","",$opt);
268 if(isset($this->sudoOption[$opt][$id])){
269 unset($this->sudoOption[$opt][$id]);
270 }
271 if(!count($this->sudoOption[$opt])){
272 unset($this->sudoOption[$opt]);
273 }
274 break;
275 }
276 }
277 }
280 $smarty = get_smarty();
281 $smarty->assign("ACL",$this->getacl(""));
282 $smarty->assign("map",
283 set_post( array(
284 "STRING" => _("string"), "BOOLEAN" => _("boolean"),
285 "INTEGER" => _("integer") , "BOOL_INTEGER" => _("integer")."-"._("boolean") ,
286 "STRING_BOOL" => _("string")."-"._("boolean"),"LISTS" => _("list"))));
287 $smarty->assign("sudoOption",$this->prepare_for_html($this->sudoOption));
288 $smarty->assign("options",$this->options);
289 return($smarty->fetch(get_template_path('options.tpl', TRUE)));
290 }
293 /*! \brief Prepare options array to be used in HTML.
294 @param Array The options array ($this->sudoOption)
295 @return Array HTML ready sudoOption. Can now be used in smarty templates
296 */
297 function prepare_for_html($a_options)
298 {
299 foreach($a_options as $name => $options){
300 foreach($options as $key => $option){
301 $a_options[set_post($name)][$key]['VALUE'] = set_post($option['VALUE']);
302 }
303 }
304 return($a_options);
305 }
308 /*! \brief Removes this plugin
309 */
310 function remove_from_parent()
311 {
312 }
315 /*! \brief Saves all relevant HTML post values for this plugin
316 */
317 function save_object()
318 {
319 if($this->acl_is_writeable("")){
320 plugin::save_object();
322 if(isset($_POST['add_option']) && isset($_POST['option'])){
323 $opt = get_post("option");
325 /* Append attribute only once, lists are handled below */
326 if(isset($this->options[$opt])){
327 $type = $this->options[$opt]['TYPE'];
328 $val = $this->options[$opt]['DEFAULT'];
329 $option = array("NAME" => $opt, "VALUE" => $val , "NEGATE" => FALSE);
330 $this->sudoOption[$opt][] = $option;
331 }
332 }
334 foreach($this->sudoOption as $name => $opts){
335 foreach($opts as $id => $opt){
337 /****
338 Get posted value for BOOLEAN
339 ****/
340 if(in_array_strict($this->options[$name]['TYPE'],array("BOOLEAN"))){
341 if(isset($_POST['option_value__'.$name.'_'.$id])){
342 $this->sudoOption[$name][$id]["VALUE"] = get_post('option_value__'.$name.'_'.$id);
343 }
344 }
346 /****
347 Get posted value for STRING / INTEGER
348 ****/
349 if(in_array_strict($this->options[$name]['TYPE'],array("STRING","INTEGER"))){
350 if(isset($_POST['option_value__'.$name.'_'.$id])){
351 $this->sudoOption[$name][$id]["VALUE"] = get_post('option_value__'.$name.'_'.$id);
352 }
353 }
355 /****
356 Get posted value for STRING_BOOL / BOOL_INTEGER
357 ****/
358 if(in_array_strict($this->options[$name]['TYPE'],array("BOOL_INTEGER","STRING_BOOL"))){
359 if(isset($_POST['option_selection__'.$name.'_'.$id])){
360 $sel = get_post('option_selection__'.$name.'_'.$id);
361 $val = "";
362 if(isset($_POST['option_value__'.$name.'_'.$id])){
363 $val = get_post('option_value__'.$name.'_'.$id);
364 }
366 if($sel == "FALSE" || $sel == "TRUE"){
367 $this->sudoOption[$name][$id]['VALUE'] = $sel;
368 $this->sudoOption[$name][$id]['NEGATE'] = FALSE;
369 }else{
370 $this->sudoOption[$name][$id]['VALUE'] = $val;
371 }
372 }
373 }
375 /****
376 Get posted value for LISTS
377 ****/
378 if(in_array_strict($this->options[$name]['TYPE'],array("LISTS"))){
379 foreach($this->sudoOption[$name] as $entry_key => $entry){
380 if(isset($_POST['list_value__'.$name.'_'.$entry_key])){
381 $val = get_post('list_value__'.$name.'_'.$entry_key);
382 $this->sudoOption[$name][$entry_key]["VALUE"] = $val;
383 }
384 }
385 }
386 }
387 }
388 }
389 }
392 /*! \brief Save changes to ldap
393 */
394 function save()
395 {
396 plugin::save();
398 $this->attrs['sudoOption'] = array();
399 foreach($this->sudoOption as $name => $opts){
400 foreach($opts as $id => $opt){
402 $type = $this->options[$name]['TYPE'];
403 $neg = $opt['NEGATE'];
404 $value = $opt['VALUE'];
405 $option = "";
407 /* Escape special chars */
408 $value = $this->escape_command($value);
410 /****
411 Save LISTS
412 ****/
413 if($type=="LISTS"){
414 if($value == ""){
415 $option = $name;
416 }else{
417 $option = $name."=".$value;
418 }
419 if($neg){
420 $option = "!".$option;
421 }
422 }
424 /****
425 Save BOOLEAN
426 ****/
427 if(in_array_strict($type,array("BOOLEAN"))){
428 $option = $name;
429 if($value == "FALSE"){
430 $option = "!".$option;
431 }
432 }
434 /****
435 Save STRING / INTEGER
436 ****/
437 if(in_array_strict($type,array("STRING","INTEGER"))){
438 if($value != ""){
439 $option = $name."=".$value;
440 }else{
441 $option = $name;
442 }
443 if($neg){
444 $option = "!".$option;
445 }
446 }
448 /****
449 Save STRING_BOOL / BOOL_INTEGER
450 ****/
451 if(in_array_strict($type,array("STRING_BOOL","BOOL_INTEGER"))){
452 if($value == "FALSE"){
453 $option = "!".$name;
454 }elseif($value == "TRUE"){
455 $option = $name;
456 }else{
457 if($value != ""){
458 $option = $name."=".$value;
459 }else{
460 $option = $name;
461 }
462 if($neg){
463 $option = "!".$option;
464 }
465 }
466 }
468 $this->attrs['sudoOption'][] = $option;
469 }
470 }
471 $this->cleanup();
472 $ldap = $this->config->get_ldap_link();
473 $ldap->cd($this->dn);
474 $ldap->modify($this->attrs);;
475 }
478 /*! \brief Checks input validity
479 */
480 function check()
481 {
482 $message = plugin::check();
484 foreach($this->sudoOption as $name => $options){
485 foreach($options as $id => $option){
486 switch($this->options[$name]['TYPE']){
488 /* Check for a valid integer value */
489 case 'INTEGER' :
490 {
491 if(!preg_match("/^[0-9]*$/",$option['VALUE'])){
492 $message[] = msgPool::invalid($name,$option['VALUE'],"/[0-9]/");
493 }
494 } break;
495 }
496 }
497 }
498 return ($message);
499 }
502 /*! \brief This function will be called if an object gets copied.
503 This function adapts attributes from the source object.
504 @param Array The source object.
505 */
506 function PrepareForCopyPaste($source)
507 {
508 plugin::PrepareForCopyPaste($source);
509 if(isset($source['sudoOption'])){
510 $this->attrs['sudoOption'] = $source['sudoOption'];
511 $this->load_options();
512 }
513 }
516 /*! \brief Escape special chars in function parameters.
517 @param String the string to that must be escaped.
518 */
519 private function escape_command($str)
520 {
521 /* Check if given value is a command (/[a-z]/ ..)
522 */
523 if(preg_match("/^\//",$str)){
524 $cmd = preg_replace("/^([^ ]*).*$/","\\1",$str);
525 $val = preg_replace("/^[^ ]*(.*)$/","\\1",$str);
526 $str = $cmd.addcslashes($val,":.,\\");
527 }
528 return($str);
529 }
532 /*! \brief Add ACL object
533 @return Returns the ACL object.
534 */
535 static function plInfo()
536 {
537 return (array(
538 "plShortName" => _("Options"),
539 "plDescription" => _("Sudo options"),
540 "plSelfModify" => FALSE,
541 "plDepends" => array(),
542 "plPriority" => 2,
543 "plSection" => array("administration"),
544 "plCategory" => array("sudo"),
545 "plProvidedAcls" => array(
546 "sudoOption" => _("Sudo options")
547 )
548 ));
549 }
551 /*! \brief Unescape special chars in function parameters.
552 @param String the string to that must be unescaped.
553 */
554 private function unescape_command($str)
555 {
556 /* Check if given value is a command (/[a-z]/ ..)
557 */
558 if(preg_match("/^\//",$str)){
559 $cmd = preg_replace("/^([^ ]*).*$/","\\1",$str);
560 $val = preg_replace("/^[^ ]*(.*)$/","\\1",$str);
561 $val = preg_replace(array("/\\\\\\\\/","/\\\\,/","/\\\\:/","/\\\\=/"),
562 array("\\",",",":","="),$val);
563 $str = $cmd.$val;
564 }
565 return($str);
566 }
567 }
568 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
569 ?>