1<?php 2 3/** 4 * DNS Library for handling lookups and updates. 5 * 6 * Copyright (c) 2020, Mike Pultz <mike@mikepultz.com>. All rights reserved. 7 * 8 * See LICENSE for more details. 9 * 10 * @category Networking 11 * @package Net_DNS2 12 * @author Mike Pultz <mike@mikepultz.com> 13 * @copyright 2020 Mike Pultz <mike@mikepultz.com> 14 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 15 * @link https://netdns2.com/ 16 * @since File available since Release 0.6.0 17 * 18 * This file contains code based off the Net::DNS::SEC Perl module by Olaf M. Kolkman 19 * 20 * This is the copyright notice from the PERL Net::DNS::SEC module: 21 * 22 * Copyright (c) 2001 - 2005 RIPE NCC. Author Olaf M. Kolkman 23 * Copyright (c) 2007 - 2008 NLnet Labs. Author Olaf M. Kolkman 24 * <olaf@net-dns.org> 25 * 26 * All Rights Reserved 27 * 28 * Permission to use, copy, modify, and distribute this software and its 29 * documentation for any purpose and without fee is hereby granted, 30 * provided that the above copyright notice appear in all copies and that 31 * both that copyright notice and this permission notice appear in 32 * supporting documentation, and that the name of the author not be 33 * used in advertising or publicity pertaining to distribution of the 34 * software without specific, written prior permission. 35 * 36 * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 37 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 38 * AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 39 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 40 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 41 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 42 * 43 */ 44 45/** 46 * SIG Resource Record - RFC2535 section 4.1 47 * 48 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 50 * | Type Covered | Algorithm | Labels | 51 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 52 * | Original TTL | 53 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 54 * | Signature Expiration | 55 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 56 * | Signature Inception | 57 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 58 * | Key Tag | / 59 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Signer's Name / 60 * / / 61 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 62 * / / 63 * / Signature / 64 * / / 65 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 66 * 67 */ 68class Net_DNS2_RR_SIG extends Net_DNS2_RR 69{ 70 /* 71 * and instance of a Net_DNS2_PrivateKey object 72 */ 73 public $private_key = null; 74 75 /* 76 * the RR type covered by this signature 77 */ 78 public $typecovered; 79 80 /* 81 * the algorithm used for the signature 82 */ 83 public $algorithm; 84 85 /* 86 * the number of labels in the name 87 */ 88 public $labels; 89 90 /* 91 * the original TTL 92 */ 93 public $origttl; 94 95 /* 96 * the signature expiration 97 */ 98 public $sigexp; 99 100 /* 101 * the inception of the signature 102 */ 103 public $sigincep; 104 105 /* 106 * the keytag used 107 */ 108 public $keytag; 109 110 /* 111 * the signer's name 112 */ 113 public $signname; 114 115 /* 116 * the signature 117 */ 118 public $signature; 119 120 /** 121 * method to return the rdata portion of the packet as a string 122 * 123 * @return string 124 * @access protected 125 * 126 */ 127 protected function rrToString() 128 { 129 return $this->typecovered . ' ' . $this->algorithm . ' ' . 130 $this->labels . ' ' . $this->origttl . ' ' . 131 $this->sigexp . ' ' . $this->sigincep . ' ' . 132 $this->keytag . ' ' . $this->cleanString($this->signname) . '. ' . 133 $this->signature; 134 } 135 136 /** 137 * parses the rdata portion from a standard DNS config line 138 * 139 * @param array $rdata a string split line of values for the rdata 140 * 141 * @return boolean 142 * @access protected 143 * 144 */ 145 protected function rrFromString(array $rdata) 146 { 147 $this->typecovered = strtoupper(array_shift($rdata)); 148 $this->algorithm = array_shift($rdata); 149 $this->labels = array_shift($rdata); 150 $this->origttl = array_shift($rdata); 151 $this->sigexp = array_shift($rdata); 152 $this->sigincep = array_shift($rdata); 153 $this->keytag = array_shift($rdata); 154 $this->signname = $this->cleanString(array_shift($rdata)); 155 156 foreach ($rdata as $line) { 157 158 $this->signature .= $line; 159 } 160 161 $this->signature = trim($this->signature); 162 163 return true; 164 } 165 166 /** 167 * parses the rdata of the Net_DNS2_Packet object 168 * 169 * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet to parse the RR from 170 * 171 * @return boolean 172 * @access protected 173 * 174 */ 175 protected function rrSet(Net_DNS2_Packet &$packet) 176 { 177 if ($this->rdlength > 0) { 178 179 // 180 // unpack 181 // 182 $x = unpack( 183 'ntc/Calgorithm/Clabels/Norigttl/Nsigexp/Nsigincep/nkeytag', 184 $this->rdata 185 ); 186 187 $this->typecovered = Net_DNS2_Lookups::$rr_types_by_id[$x['tc']]; 188 $this->algorithm = $x['algorithm']; 189 $this->labels = $x['labels']; 190 $this->origttl = Net_DNS2::expandUint32($x['origttl']); 191 192 // 193 // the dates are in GM time 194 // 195 $this->sigexp = gmdate('YmdHis', $x['sigexp']); 196 $this->sigincep = gmdate('YmdHis', $x['sigincep']); 197 198 // 199 // get the keytag 200 // 201 $this->keytag = $x['keytag']; 202 203 // 204 // get teh signers name and signature 205 // 206 $offset = $packet->offset + 18; 207 $sigoffset = $offset; 208 209 $this->signname = strtolower( 210 Net_DNS2_Packet::expand($packet, $sigoffset) 211 ); 212 $this->signature = base64_encode( 213 substr($this->rdata, 18 + ($sigoffset - $offset)) 214 ); 215 216 return true; 217 } 218 219 return false; 220 } 221 222 /** 223 * returns the rdata portion of the DNS packet 224 * 225 * @param Net_DNS2_Packet &$packet a Net_DNS2_Packet packet use for 226 * compressed names 227 * 228 * @return mixed either returns a binary packed 229 * string or null on failure 230 * @access protected 231 * 232 */ 233 protected function rrGet(Net_DNS2_Packet &$packet) 234 { 235 // 236 // parse the values out of the dates 237 // 238 preg_match( 239 '/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/', $this->sigexp, $e 240 ); 241 preg_match( 242 '/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/', $this->sigincep, $i 243 ); 244 245 // 246 // pack the value 247 // 248 $data = pack( 249 'nCCNNNn', 250 Net_DNS2_Lookups::$rr_types_by_name[$this->typecovered], 251 $this->algorithm, 252 $this->labels, 253 $this->origttl, 254 gmmktime($e[4], $e[5], $e[6], $e[2], $e[3], $e[1]), 255 gmmktime($i[4], $i[5], $i[6], $i[2], $i[3], $i[1]), 256 $this->keytag 257 ); 258 259 // 260 // the signer name is special; it's not allowed to be compressed 261 // (see section 3.1.7) 262 // 263 $names = explode('.', strtolower($this->signname)); 264 foreach ($names as $name) { 265 266 $data .= chr(strlen($name)); 267 $data .= $name; 268 } 269 270 $data .= chr('0'); 271 272 // 273 // if the signature is empty, and $this->private_key is an instance of a 274 // private key object, and we have access to openssl, then assume this 275 // is a SIG(0), and generate a new signature 276 // 277 if ( (strlen($this->signature) == 0) 278 && ($this->private_key instanceof Net_DNS2_PrivateKey) 279 && (extension_loaded('openssl') === true) 280 ) { 281 282 // 283 // create a new packet for the signature- 284 // 285 $new_packet = new Net_DNS2_Packet_Request('example.com', 'SOA', 'IN'); 286 287 // 288 // copy the packet data over 289 // 290 $new_packet->copy($packet); 291 292 // 293 // remove the SIG object from the additional list 294 // 295 array_pop($new_packet->additional); 296 $new_packet->header->arcount = count($new_packet->additional); 297 298 // 299 // copy out the data 300 // 301 $sigdata = $data . $new_packet->get(); 302 303 // 304 // based on the algorithm 305 // 306 $algorithm = 0; 307 308 switch($this->algorithm) { 309 310 // 311 // MD5 312 // 313 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSAMD5: 314 315 $algorithm = OPENSSL_ALGO_MD5; 316 break; 317 318 // 319 // SHA1 320 // 321 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA1: 322 323 $algorithm = OPENSSL_ALGO_SHA1; 324 break; 325 326 // 327 // SHA256 (PHP 5.4.8 or higher) 328 // 329 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA256: 330 331 if (version_compare(PHP_VERSION, '5.4.8', '<') == true) { 332 333 throw new Net_DNS2_Exception( 334 'SHA256 support is only available in PHP >= 5.4.8', 335 Net_DNS2_Lookups::E_OPENSSL_INV_ALGO 336 ); 337 } 338 339 $algorithm = OPENSSL_ALGO_SHA256; 340 break; 341 342 // 343 // SHA512 (PHP 5.4.8 or higher) 344 // 345 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA512: 346 347 if (version_compare(PHP_VERSION, '5.4.8', '<') == true) { 348 349 throw new Net_DNS2_Exception( 350 'SHA512 support is only available in PHP >= 5.4.8', 351 Net_DNS2_Lookups::E_OPENSSL_INV_ALGO 352 ); 353 } 354 355 $algorithm = OPENSSL_ALGO_SHA512; 356 break; 357 358 // 359 // unsupported at the moment 360 // 361 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_DSA: 362 case Net_DNS2_Lookups::DSNSEC_ALGORITHM_RSASHA1NSEC3SHA1: 363 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_DSANSEC3SHA1: 364 default: 365 throw new Net_DNS2_Exception( 366 'invalid or unsupported algorithm', 367 Net_DNS2_Lookups::E_OPENSSL_INV_ALGO 368 ); 369 break; 370 } 371 372 // 373 // sign the data 374 // 375 if (openssl_sign($sigdata, $this->signature, $this->private_key->instance, $algorithm) == false) { 376 377 throw new Net_DNS2_Exception( 378 openssl_error_string(), 379 Net_DNS2_Lookups::E_OPENSSL_ERROR 380 ); 381 } 382 383 // 384 // build the signature value based 385 // 386 switch($this->algorithm) { 387 388 // 389 // RSA- add it directly 390 // 391 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSAMD5: 392 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA1: 393 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA256: 394 case Net_DNS2_Lookups::DNSSEC_ALGORITHM_RSASHA512: 395 396 $this->signature = base64_encode($this->signature); 397 break; 398 } 399 } 400 401 // 402 // add the signature 403 // 404 $data .= base64_decode($this->signature); 405 406 $packet->offset += strlen($data); 407 408 return $data; 409 } 410} 411