[gosa.git] / gosa-plugins / mit-krb5 / admin / systems / services / kerberos / class_password-methods-MIT.inc
1 <?php
2 /*
3 This code is part of GOsa (https://gosa.gonicus.de)
4 Copyright (C) 2008 Fabian Hickert
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
21 class passwordMethodMIT extends passwordMethod
22 {
24 var $dn = "new"; // DN of the current object
25 var $parent_dn = "new"; // parents DN
26 var $is_account = FALSE; // This is TRUE if this object already has a krb extension
27 var $server_list = array(); // A list with all configured servers
28 var $map = array(); // Mapping array, maps SERVER-REALM, REALM-SERVER ...
30 var $goKrbRealm = ""; // The realm name this principal belongs to
31 var $principal = ""; // The principals name (e.g. user@MY-DOMAIN.SYS)
32 var $is_new = TRUE; // Is TRUE if principal is new
34 var $si_error = FALSE; // TRUE is daemon communication failed
35 var $si_error_msg = ""; // The last error message if above attribute is TRUE.
37 var $values = array(
38 "PRINC_EXPIRE_TIME", // Expiry date of this principal
39 "PW_EXPIRATION", // Password expiration
40 "MAX_LIFE", // Ticket lifetime
41 "MASK", // I'dont know
42 "MAX_RENEWABLE_LIFE", // Max ticket lifetime when renewed
43 "POLICY"); // The policy used by this principal
45 var $PRINC_EXPIRE_TIME = 0;
46 var $PW_EXPIRATION = 0;
47 var $PRINC_EXPIRE_TIME_clear = TRUE;
48 var $PW_EXPIRATION_clear = TRUE;
49 var $MAX_LIFE = 36000;
50 var $MAX_RENEWABLE_LIFE = 604800;
51 var $MASK = 0;
53 var $flags = array(
54 "DISALLOW_POSTDATED" =>0x00000001 , // Pohibit postdated tickets
55 "DISALLOW_FORWARDABLE" =>0x00000002 , // Prohibit forwardable tickets
56 "DISALLOW_TGT_BASED" =>0x00000004 , // Disallow Ticket-Granting Service
57 "DISALLOW_RENEWABLE" =>0x00000008 , // Prohibit renewable tickets
58 "DISALLOW_PROXIABLE" =>0x00000010 , // Disallow proxiable tickets
59 "DISALLOW_DUP_SKEY" =>0x00000020 , // Disallow user to user authentification
60 "DISALLOW_ALL_TIX" =>0x00000040 , // Forbid ticket issuance
61 "REQUIRES_PRE_AUTH" =>0x00000080 , // Preauthentication required
62 "REQUIRES_HW_AUTH" =>0x00000100 , // Hardware preauthentication
63 "REQUIRES_PWCHANGE" =>0x00000200 , // Force a password change
64 "UNKNOWN_0x00000400" =>0x00000400 , // ?
65 "UNKNOWN_0x00000800" =>0x00000800 , // ?
66 "DISALLOW_SVR" =>0x00001000 , // Prohibit issuance of service tickets
67 "PWCHANGE_SERVICE" =>0x00002000 , // Password change service
68 "SUPPORT_DESMD5" =>0x00004000 , // ?
69 "NEW_PRINC" =>0x00008000 ); // ?
71 var $used_flags = 128; // Flags, see below
73 var $readonly = array(
74 "FAIL_AUTH_COUNT", // The number of failed logins
75 "KVNO", // Key version number
76 "LAST_FAILED", // Last failed login time
77 "LAST_PWD_CHANGE", // Password last change time
78 "LAST_SUCCESS", // Last successful login
79 "MOD_DATE"); // Last modification time
81 var $FAIL_AUTH_COUNT = 0;
82 var $KVNO = "";
83 var $LAST_FAILED = 0;
84 var $LAST_PWD_CHANGE = 0;
85 var $LAST_SUCCESS = 0;
86 var $MOD_DATE = 0;
88 var $POLICY = "";
89 var $POLICIES = array(); // Policies provided by the corrently selected realm/server
92 public function __construct(&$config,$dn = "new")
93 {
94 $this->config= $config;
95 $this->parent_dn = $dn;
97 /* No config object given, this may be the case
98 if there is only a is_available() request triggered.
99 */
100 if(!is_object($config)){
101 return;
102 }
104 /* Keep the cached valued and skip loading principals
105 from si until this method gets configured.
106 */
107 $skip_si_access = TRUE;
108 if($dn != "new" && $dn != ""){
109 session::un_set("MIT_CACHE");
110 session::un_set("MIT_PRINCIPAL_CACHE");
111 session::un_set("MIT_POLICY_CACHE");
112 $this->clear_cache();
113 $skip_si_access = FALSE;
114 }
116 /* Get a list of all kerberos servers, defined in ldap
117 and get a list of principals they are providing.
118 */
119 $ldap = $this->config->get_ldap_link();
120 $ldap->cd($this->config->current['BASE']);
121 $ldap->search("(&(objectClass=goServer)(objectClass=goKrbServer))",array("goKrbRealm","cn","description","macAddress"));
122 $this->server_list = array();
123 while($attrs = $ldap->fetch()){
124 if(!isset($attrs['macAddress'][0])) continue;
125 if(!isset($attrs['description'][0])) $attrs['description'][0] ="";
127 if($skip_si_access){
128 $principals = array();
129 }else{
130 $principals = $this->load_principals_for_server($attrs['macAddress'][0]);
131 }
133 /* Create Realm/Server/Principal mapping.
134 */
135 foreach($principals as $principal){
136 $this->map["PRINCIPAL_SERVER"][$principal] = $attrs['cn'][0];
137 $this->map["PRINCIPAL_REALM"] [$principal] = $attrs['goKrbRealm'][0];
138 }
139 $this->map["SERVER_REALM"][$attrs['cn'][0]] = $attrs['goKrbRealm'][0];
140 $this->map["REALM_SERVER"][$attrs['goKrbRealm'][0]] = $attrs['cn'][0];
142 /* Set first realm as selected.
143 */
144 if($this->goKrbRealm == ""){
145 $this->goKrbRealm = $attrs['goKrbRealm'][0];
146 }
148 /* Create Server list
149 */
150 $this->server_list[$attrs['cn'][0]] = array("macAddress" => $attrs['macAddress'][0],
151 "description"=> $attrs['description'][0],
152 "dn" => $attrs['dn'],
153 "principals" => $principals,
154 "goKrbRealm" => $attrs['goKrbRealm'][0],
155 "cn" => $attrs['cn'][0]);
156 }
158 /* If this methods is initialized with a valid object dn then
159 load the object data from ldap and the SI daemon && initialize this class.
160 */
161 $this->is_new = TRUE;
162 if(!$skip_si_access){
163 $ldap = $this->config->get_ldap_link();
164 $ldap->cd($dn);
165 $ldap->cat($dn);
166 $this->attrs = $ldap->fetch();
168 /* Set initial pwd hash which take effect if no password method was set yet.
169 Will be overwritten by the following lines, if the user has already a valid principal.
170 */
171 $this->principal = $this->attrs['uid'][0]."@".$this->goKrbRealm;
173 if(isset($this->attrs['userPassword']) && preg_match("/^\{".$this->get_hash_name()."\}/",$this->attrs['userPassword'][0])){
175 /* Extract principal name out of userPassword attribute
176 */
177 $p_name = preg_replace("/^\{".$this->get_hash_name()."\}/","",$this->attrs['userPassword'][0]);
179 /* Try to detect server our principal is configured on
180 */
181 if(isset($this->map['PRINCIPAL_SERVER'][$p_name])){
182 $server= $this->map['PRINCIPAL_SERVER'][$p_name];
183 $this->goKrbRealm = $this->map['SERVER_REALM'][$server];
184 $this->principal = $p_name;
186 /* Load policies */
187 $server_name = $this->map['REALM_SERVER'][$this->goKrbRealm];
188 $server_mac = $this->server_list[$server_name]['macAddress'];
189 $this->POLICIES = $this->load_policies_for_server($server_mac);
191 /* Load principal */
192 $this->load_principal($this->server_list[$server]['macAddress'],$p_name);
193 $this->is_new = FALSE;
194 }
195 }
196 }
197 }
200 public static function clear_cache()
201 {
202 session::un_set("MIT_CACHE");
203 session::un_set("MIT_PRINCIPAL_CACHE");
204 session::un_set("MIT_POLICY_CACHE");
205 }
208 /*! \brief Load a specific principal from the si daemon
209 and initialize this plugin with it.
210 @param String The macAddress of the kerberos server.
211 @param String The name of the principal to load.
212 */
213 public function load_principal($server,$name)
214 {
215 $o = new gosaSupportDaemon();
216 $tmp = array();
217 $tmp = $o->krb5_get_principal($server,$name);
219 if($o->is_error()){
220 $this->si_error = TRUE;
221 $this->si_error_msg = sprintf(_("Cannot load principal '%s', from server '%s'!"),$name,$server).": <br>".$o->get_error();
222 msg_dialog::display(_("Service infrastructure"),msgPool::siError($o->get_error()),ERROR_DIALOG);
223 }else{
225 /* Load flags
226 */
227 if(isset($tmp['ATTRIBUTES'])){
228 $this->used_flags = $tmp['ATTRIBUTES'];
229 }
231 /* Load readonly attributes
232 */
233 foreach($this->readonly as $attr){
234 if(isset($tmp[$attr])){
235 $this->$attr = $tmp[$attr];
236 }
237 }
239 /* Load modifyable attributes
240 */
241 foreach($this->values as $attr){
242 if(isset($tmp[$attr])){
243 $this->$attr = $tmp[$attr];
244 }
245 }
247 /* Update time checkboxes
248 */
249 $date_values = array("PW_EXPIRATION","PRINC_EXPIRE_TIME");
250 foreach($date_values as $value){
251 if(!empty($this->$value)){
252 $clear = $value."_clear";
253 $this->$clear = FALSE;
254 }
255 }
256 }
257 }
260 /*! \brief Get the list of all configured principals for a given server.
261 @param String The servers mac address.
262 @return Array A list with all principals
263 The results will cached.
264 */
265 public function load_principals_for_server($server)
266 {
267 if(!session::is_set("MIT_PRINCIPAL_CACHE")){
268 session::set("MIT_PRINCIPAL_CACHE",array());
269 }
270 $cache = session::get("MIT_PRINCIPAL_CACHE");
271 if(!isset($cache[$server])){
272 $o = new gosaSupportDaemon();
273 $tmp = $o->krb5_list_principals($server);
274 if($o->is_error()){
275 $this->si_error = TRUE;
276 $this->si_error_msg = sprintf(_("Cannot load principals from server '%s'!"),$server).": <br>".$o->get_error();
277 msg_dialog::display(_("Service infrastructure"),msgPool::siError($o->get_error()),ERROR_DIALOG);
278 return(array());
279 }else{
280 $cache[$server] = $tmp;
281 }
282 session::set("MIT_PRINCIPAL_CACHE",$cache);
283 }
284 return($cache[$server]);
285 }
288 /*! \brief get list of all configured policies
289 for a given server.
290 The results will cached.
291 */
292 public function load_policies_for_server($server)
293 {
294 if(!session::is_set("MIT_POLICY_CACHE")){
295 session::set("MIT_POLICY_CACHE",array());
296 }
297 $cache = session::get("MIT_POLICY_CACHE");
298 if(!isset($cache[$server])){
299 $o = new gosaSupportDaemon();
300 $tmp = $o->krb5_list_policies($server);
301 if($o->is_error()){
302 $this->si_error = TRUE;
303 $this->si_error_msg = sprintf(_("Cannot load policies from server '%s'!"),$server).": <br>".$o->get_error();
304 msg_dialog::display(_("Service infrastructure"),msgPool::siError($o->get_error()),ERROR_DIALOG);
305 return(array());
306 }else{
307 $cache[$server] = array();
308 $cache[$server]["_none_"] = _("none");
309 foreach($tmp as $policy){
310 $cache[$server][$policy] = $policy;
311 }
312 ksort($cache[$server]);
313 }
314 session::set("MIT_POLICY_CACHE",$cache);
315 }
316 return($cache[$server]);
317 }
320 /*! \brief Check if this password method is useable.
321 This is the case if there is a si server running and at least one server configured.
322 kerberos support.
323 */
324 public function is_available()
325 {
326 $o = new gosaSupportDaemon(FALSE);
327 if(count($this->server_list) && $o->connect()){
328 return TRUE;
329 }
330 return(FALSE);
331 }
334 /*! \brief Create the password hash. In this case: {kerberos/sasl}name@RELAM
335 @param String The password -in this case unusued.
336 @return String The generated hash
337 */
338 public function generate_hash($pwd = "")
339 {
340 $mode= "kerberos";
341 if ($this->config->get_cfg_value("krbsasl") == "true"){
342 $mode= "sasl";
343 }
344 return "{".$mode."}".$this->attrs['uid'][0]."@".$this->goKrbRealm;
345 }
348 /*! \brief Removes this principal.
349 */
350 public function remove_from_parent()
351 {
352 if(!empty($this->principal) && $this->goKrbRealm){
353 $server = $this->map['REALM_SERVER'][$this->goKrbRealm];
354 $o = new gosaSupportDaemon();
355 if(!$o->krb5_del_principal($this->server_list[$server]['macAddress'],$this->principal)){
356 $this->si_error = TRUE;
357 $this->si_error_msg = $o->get_error();
358 msg_dialog::display(_("Service infrastructure"),msgPool::siError($o->get_error()),ERROR_DIALOG);
359 }
360 }
361 }
364 /*! \brief Set a new password for this principal
365 @param String The new password.
366 */
367 public function set_password($password)
368 {
369 if(!empty($this->principal) && $this->goKrbRealm){
370 $server = $this->map['REALM_SERVER'][$this->goKrbRealm];
371 $o = new gosaSupportDaemon();
372 if(!$o->krb5_set_password($this->server_list[$server]['macAddress'],$this->principal,$password)){
373 msg_dialog::display(_("Service infrastructure"),msgPool::siError($o->get_error()),ERROR_DIALOG);
374 return(FALSE);
375 }
376 }
377 return(TRUE);
378 }
381 /*! \brief Return the hash name of this mehtod, e.g. to identify methods.
382 @return String The hash used by this method.
383 */
384 public function get_hash_name()
385 {
386 $mode= "kerberos";
387 if ($this->config->get_cfg_value("krbsasl") == "true"){
388 $mode= "sasl";
389 }
390 return "$mode";
391 }
394 /*! \brief Returns TRUE if this method is configurable else FALSE
395 @retrun Boolena TRUE if configurable, else FALSE.
396 */
397 public function is_configurable()
398 {
399 return TRUE;
400 }
403 /*! \brief Additional info displayed in the users password method drop down.
404 @retunr String Additional password method info.
405 */
406 public function get_description()
407 {
408 return(_("Daemon based"));
409 }
412 /*! \brief Display a HTML based configuration dialog for this plugin
413 @return String HTML.
414 */
415 public function configure()
416 {
417 $this->save_object();
419 $years = array();
420 $start = date("Y")-1;
421 for($i = $start; $i < ($start +20) ; $i++){
422 $years[$i] = $i;
423 }
424 $month= array();
425 for($i = 1; $i <= 12 ; $i++){
426 $month[str_pad($i,2,"0",STR_PAD_LEFT)] = $i;
427 }
428 $days= array();
429 for($i = 1; $i <= 31 ; $i++){
430 $days[str_pad($i,2,"0",STR_PAD_LEFT)] = $i;
431 }
432 $hours= array();
433 for($i = 0; $i <= 23 ; $i++){
434 $hours[str_pad($i,2,"0",STR_PAD_LEFT)] = $i;
435 }
436 $minutes= array();
437 for($i = 0; $i <= 59 ; $i++){
438 $minutes[str_pad($i,2,"0",STR_PAD_LEFT)] = $i;
439 }
442 /* Cancel heimdal options */
443 if (isset($_POST['pw_abort']) || $this->display == FALSE){
444 $this->display = FALSE;
445 return("");
446 }
448 /* Cancel heimdal options */
449 if (isset($_POST['pw_save'])){
450 $msgs = $this->check();
451 if(count($msgs)){
452 foreach($msgs as $msg){
453 msg_dialog::display(_("Kerberos"),$msg,WARNING_DIALOG);
454 }
455 }else{
456 $this->display = FALSE;
457 return "";
458 }
459 }
461 $smarty = get_smarty();
462 $smarty->assign("si_error",$this->si_error);
463 $smarty->assign("si_error_msg",$this->si_error_msg);
464 $smarty->assign("years",$years);
465 $smarty->assign("month",$month);
466 $smarty->assign("days",$days);
467 $smarty->assign("hours",$hours);
468 $smarty->assign("minutes",$minutes);
469 $smarty->assign("server_list",$this->server_list);
470 $smarty->assign("POLICY" ,$this->POLICY);
471 $smarty->assign("goKrbRealm" , $this->goKrbRealm);
472 $server_name = $this->map['REALM_SERVER'][$this->goKrbRealm];
473 $server_mac = $this->server_list[$server_name]['macAddress'];
474 $this->POLICIES = $this->load_policies_for_server($server_mac);
475 $smarty->assign("POLICIES" ,$this->POLICIES);
477 foreach($this->values as $attr){
478 $smarty->assign($attr ,$this->$attr);
479 }
480 foreach($this->readonly as $attr){
481 $smarty->assign($attr ,$this->$attr);
482 }
483 foreach($this->flags as $attr => $hex){
484 $smarty->assign($attr, ($this->used_flags & $hex ));
485 }
487 $date_values = array("PRINC_EXPIRE_TIME","PW_EXPIRATION");
488 foreach($date_values as $date_val){
489 $clear = $date_val."_clear";
490 $smarty->assign($date_val."_clear",$this->$clear);
491 $smarty->assign($date_val."_y",date("Y",$this->$date_val));
492 $smarty->assign($date_val."_m",date("m",$this->$date_val));
493 $smarty->assign($date_val."_d",date("d",$this->$date_val));
494 $smarty->assign($date_val."_h",date("h",$this->$date_val));
495 $smarty->assign($date_val."_i",date("i",$this->$date_val));
496 }
498 return($smarty->fetch(get_template_path("pwd_kerberos_mit.tpl",TRUE,dirname(__FILE__))));
499 }
502 /*! \brief Saves all relevant HTML posts for this plugin
503 */
504 public function save_object()
505 {
506 /* If the communication with the si server failed,
507 you are able to retry to connect to the server.
508 Here we hanlde those requests.
509 */
510 if(isset($_POST['retry_si'])){
511 $this->si_error= FALSE;
512 $this->si_error_msg= "";
513 session::un_set("MIT_PRINCIPAL_CACHE");
514 session::un_set("MIT_POLICY_CACHE");
515 $this->__construct($this->config,$this->parent_dn);
516 }
518 /* Only handle posts for this plugin, it its content was posted
519 */
520 if(isset($_POST['pwd_heimdal_posted'])){
522 if(isset($_POST['goKrbRealm'])){
523 $this->goKrbRealm = get_post("goKrbRealm");
524 }
526 $this->used_flags = 0;
527 foreach($this->flags as $attr => $hex){
528 if(isset($_POST[$attr])){
529 $this->used_flags |= $hex;
530 }
531 }
533 foreach(array("MAX_LIFE","MAX_RENEWABLE_LIFE","POLICY") as $attr){
534 if(isset($_POST[$attr])){
535 $this->$attr = get_post($attr);
536 }
537 }
539 $date_values = array("PW_EXPIRATION","PRINC_EXPIRE_TIME");
540 foreach($date_values as $date_value){
541 $clear = $date_value."_clear";
542 if(isset($_POST[$date_value."_clear"])){
543 $this->$clear = TRUE;
544 }else{
545 $this->$clear = FALSE;
546 $this->$date_value = gmmktime(
547 $_POST[$date_value."_h"],
548 $_POST[$date_value."_i"],
549 0,
550 $_POST[$date_value."_m"],
551 $_POST[$date_value."_d"],
552 $_POST[$date_value."_y"]);
553 }
554 }
555 }
556 }
559 /*! \brief Checks the values specified in the configuration dialog.
560 @return Array Containing all error messages.
561 */
562 public function check()
563 {
564 $message = array();
566 if(!preg_match("/^[0-9]*$/",$this->MAX_LIFE)){
567 $message[] = msgPool::invalid(_("Ticket max life"),$this->MAX_LIFE,"/[0-9]/");
568 }
569 if(!preg_match("/^[0-9]*$/",$this->MAX_RENEWABLE_LIFE)){
570 $message[] = msgPool::invalid(_("Ticket max renew"),$this->MAX_RENEWABLE_LIFE,"/[0-9]/");
571 }
572 return($message);
573 }
576 /*! \brief Saves changes back to the SI daemon.
577 */
578 public function save($dn)
579 {
580 $ldap = $this->config->get_ldap_link();
581 $ldap->cd($dn);
582 $ldap->cat($dn,array('uid'));
583 $attrs = $ldap->fetch();
585 if(isset($attrs['uid'][0])){
587 /* Get servers mac */
588 $server_name = $this->map['REALM_SERVER'][$this->goKrbRealm];
589 $server_mac = $this->server_list[$server_name]['macAddress'];
591 $uid = $attrs['uid'][0];
592 $principal = $uid."@".strtoupper($this->goKrbRealm);
593 $policy = $this->POLICY;
595 /* Collect flags */
596 $flags = array();
597 $entry = array();
599 $entry['ATTRIBUTES'] = $this->used_flags;
601 /* Append other values */
602 foreach($this->values as $attr){
603 if($attr == "POLICY") continue;
604 $entry[$attr] = $this->$attr;
605 }
607 /* Prepare entry to be saved */
608 if($policy != "_none_"){
609 $entry['POLICY'] = $policy;
610 }
612 /* Set date values
613 */
614 $date_values = array("PW_EXPIRATION","PRINC_EXPIRE_TIME");
615 foreach($date_values as $value){
616 $clear = $value."_clear";
617 if($this->$clear){
618 $entry[$value] = 0;
619 }
620 }
623 /* Save principal changes */
624 $o = new gosaSupportDaemon();
625 if(in_array($principal,$this->server_list[$server_name]['principals'])){
626 $this->is_new = FALSE;
627 }
629 if($this->is_new){
630 $o->krb5_add_principal($server_mac,$principal,$entry);
631 }else{
632 $o->krb5_set_principal($server_mac,$principal,$entry);
633 }
634 if($o->is_error()){
635 $this->si_error = TRUE;
636 $this->si_error_msg = $o->get_error();
637 msg_dialog::display(_("Service infrastructure"),msgPool::siError($o->get_error()),ERROR_DIALOG);
638 }
639 }
640 }
641 }
642 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
643 ?>