1 /* 2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * Copyright (c) 1999 by Internet Software Consortium, Inc. 10 * 11 * Permission to use, copy, modify, and distribute this software for any 12 * purpose with or without fee is hereby granted, provided that the above 13 * copyright notice and this permission notice appear in all copies. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 16 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 18 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 19 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 20 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 21 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 22 * SOFTWARE. 23 */ 24 25 #ifndef lint 26 static const char rcsid[] = "$Id: ns_sign.c,v 8.12 2002/10/01 06:48:37 marka Exp $"; 27 #endif 28 29 /* Import. */ 30 31 #include "port_before.h" 32 #include "fd_setsize.h" 33 34 #include <sys/types.h> 35 #include <sys/param.h> 36 37 #include <netinet/in.h> 38 #include <arpa/nameser.h> 39 #include <arpa/inet.h> 40 41 #include <errno.h> 42 #include <netdb.h> 43 #include <resolv.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 #include <unistd.h> 49 50 #include <isc/dst.h> 51 #include <isc/assertions.h> 52 53 #include "port_after.h" 54 55 #define BOUNDS_CHECK(ptr, count) \ 56 do { \ 57 if ((ptr) + (count) > eob) { \ 58 errno = EMSGSIZE; \ 59 return(NS_TSIG_ERROR_NO_SPACE); \ 60 } \ 61 } while (0) 62 63 /* ns_sign 64 * Parameters: 65 * msg message to be sent 66 * msglen input - length of message 67 * output - length of signed message 68 * msgsize length of buffer containing message 69 * error value to put in the error field 70 * key tsig key used for signing 71 * querysig (response), the signature in the query 72 * querysiglen (response), the length of the signature in the query 73 * sig a buffer to hold the generated signature 74 * siglen input - length of signature buffer 75 * output - length of signature 76 * 77 * Errors: 78 * - bad input data (-1) 79 * - bad key / sign failed (-BADKEY) 80 * - not enough space (NS_TSIG_ERROR_NO_SPACE) 81 */ 82 int 83 ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k, 84 const u_char *querysig, int querysiglen, u_char *sig, int *siglen, 85 time_t in_timesigned) 86 { 87 return(ns_sign2(msg, msglen, msgsize, error, k, 88 querysig, querysiglen, sig, siglen, 89 in_timesigned, NULL, NULL)); 90 } 91 92 int 93 ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k, 94 const u_char *querysig, int querysiglen, u_char *sig, int *siglen, 95 time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr) 96 { 97 HEADER *hp = (HEADER *)msg; 98 DST_KEY *key = (DST_KEY *)k; 99 u_char *cp = msg + *msglen, *eob = msg + msgsize; 100 u_char *lenp; 101 u_char *alg; 102 int n; 103 time_t timesigned; 104 u_char name[NS_MAXCDNAME]; 105 106 dst_init(); 107 if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL) 108 return (-1); 109 110 /* Name. */ 111 if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { 112 n = ns_name_pton(key->dk_key_name, name, sizeof name); 113 if (n != -1) 114 n = ns_name_pack(name, cp, eob - cp, 115 (const u_char **)dnptrs, 116 (const u_char **)lastdnptr); 117 118 } else { 119 n = ns_name_pton("", name, sizeof name); 120 if (n != -1) 121 n = ns_name_pack(name, cp, eob - cp, NULL, NULL); 122 } 123 if (n < 0) 124 return (NS_TSIG_ERROR_NO_SPACE); 125 cp += n; 126 127 /* Type, class, ttl, length (not filled in yet). */ 128 BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); 129 PUTSHORT(ns_t_tsig, cp); 130 PUTSHORT(ns_c_any, cp); 131 PUTLONG(0, cp); /* TTL */ 132 lenp = cp; 133 cp += 2; 134 135 /* Alg. */ 136 if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { 137 if (key->dk_alg != KEY_HMAC_MD5) 138 return (-ns_r_badkey); 139 n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); 140 } 141 else 142 n = dn_comp("", cp, eob - cp, NULL, NULL); 143 if (n < 0) 144 return (NS_TSIG_ERROR_NO_SPACE); 145 alg = cp; 146 cp += n; 147 148 /* Time. */ 149 BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); 150 PUTSHORT(0, cp); 151 timesigned = time(NULL); 152 if (error != ns_r_badtime) 153 PUTLONG(timesigned, cp); 154 else 155 PUTLONG(in_timesigned, cp); 156 PUTSHORT(NS_TSIG_FUDGE, cp); 157 158 /* Compute the signature. */ 159 if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { 160 void *ctx; 161 u_char buf[NS_MAXCDNAME], *cp2; 162 int n; 163 164 dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); 165 166 /* Digest the query signature, if this is a response. */ 167 if (querysiglen > 0 && querysig != NULL) { 168 u_int16_t len_n = htons(querysiglen); 169 dst_sign_data(SIG_MODE_UPDATE, key, &ctx, 170 (u_char *)&len_n, INT16SZ, NULL, 0); 171 dst_sign_data(SIG_MODE_UPDATE, key, &ctx, 172 querysig, querysiglen, NULL, 0); 173 } 174 175 /* Digest the message. */ 176 dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen, 177 NULL, 0); 178 179 /* Digest the key name. */ 180 n = ns_name_ntol(name, buf, sizeof(buf)); 181 INSIST(n > 0); 182 dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); 183 184 /* Digest the class and TTL. */ 185 cp2 = buf; 186 PUTSHORT(ns_c_any, cp2); 187 PUTLONG(0, cp2); 188 dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, 189 NULL, 0); 190 191 /* Digest the algorithm. */ 192 n = ns_name_ntol(alg, buf, sizeof(buf)); 193 INSIST(n > 0); 194 dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); 195 196 /* Digest the time signed, fudge, error, and other data */ 197 cp2 = buf; 198 PUTSHORT(0, cp2); /* Top 16 bits of time */ 199 if (error != ns_r_badtime) 200 PUTLONG(timesigned, cp2); 201 else 202 PUTLONG(in_timesigned, cp2); 203 PUTSHORT(NS_TSIG_FUDGE, cp2); 204 PUTSHORT(error, cp2); /* Error */ 205 if (error != ns_r_badtime) 206 PUTSHORT(0, cp2); /* Other data length */ 207 else { 208 PUTSHORT(INT16SZ+INT32SZ, cp2); /* Other data length */ 209 PUTSHORT(0, cp2); /* Top 16 bits of time */ 210 PUTLONG(timesigned, cp2); 211 } 212 dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, 213 NULL, 0); 214 215 n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, 216 sig, *siglen); 217 if (n < 0) 218 return (-ns_r_badkey); 219 *siglen = n; 220 } else 221 *siglen = 0; 222 223 /* Add the signature. */ 224 BOUNDS_CHECK(cp, INT16SZ + (*siglen)); 225 PUTSHORT(*siglen, cp); 226 memcpy(cp, sig, *siglen); 227 cp += (*siglen); 228 229 /* The original message ID & error. */ 230 BOUNDS_CHECK(cp, INT16SZ + INT16SZ); 231 PUTSHORT(ntohs(hp->id), cp); /* already in network order */ 232 PUTSHORT(error, cp); 233 234 /* Other data. */ 235 BOUNDS_CHECK(cp, INT16SZ); 236 if (error != ns_r_badtime) 237 PUTSHORT(0, cp); /* Other data length */ 238 else { 239 PUTSHORT(INT16SZ+INT32SZ, cp); /* Other data length */ 240 BOUNDS_CHECK(cp, INT32SZ+INT16SZ); 241 PUTSHORT(0, cp); /* Top 16 bits of time */ 242 PUTLONG(timesigned, cp); 243 } 244 245 /* Go back and fill in the length. */ 246 PUTSHORT(cp - lenp - INT16SZ, lenp); 247 248 hp->arcount = htons(ntohs(hp->arcount) + 1); 249 *msglen = (cp - msg); 250 return (0); 251 } 252 253 int 254 ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen, 255 ns_tcp_tsig_state *state) 256 { 257 dst_init(); 258 if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) 259 return (-1); 260 state->counter = -1; 261 state->key = k; 262 if (state->key->dk_alg != KEY_HMAC_MD5) 263 return (-ns_r_badkey); 264 if (querysiglen > (int)sizeof(state->sig)) 265 return (-1); 266 memcpy(state->sig, querysig, querysiglen); 267 state->siglen = querysiglen; 268 return (0); 269 } 270 271 int 272 ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error, 273 ns_tcp_tsig_state *state, int done) 274 { 275 return (ns_sign_tcp2(msg, msglen, msgsize, error, state, 276 done, NULL, NULL)); 277 } 278 279 int 280 ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error, 281 ns_tcp_tsig_state *state, int done, 282 u_char **dnptrs, u_char **lastdnptr) 283 { 284 u_char *cp, *eob, *lenp; 285 u_char buf[MAXDNAME], *cp2; 286 HEADER *hp = (HEADER *)msg; 287 time_t timesigned; 288 int n; 289 290 if (msg == NULL || msglen == NULL || state == NULL) 291 return (-1); 292 293 state->counter++; 294 if (state->counter == 0) 295 return (ns_sign2(msg, msglen, msgsize, error, state->key, 296 state->sig, state->siglen, 297 state->sig, &state->siglen, 0, 298 dnptrs, lastdnptr)); 299 300 if (state->siglen > 0) { 301 u_int16_t siglen_n = htons(state->siglen); 302 dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx, 303 NULL, 0, NULL, 0); 304 dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, 305 (u_char *)&siglen_n, INT16SZ, NULL, 0); 306 dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, 307 state->sig, state->siglen, NULL, 0); 308 state->siglen = 0; 309 } 310 311 dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen, 312 NULL, 0); 313 314 if (done == 0 && (state->counter % 100 != 0)) 315 return (0); 316 317 cp = msg + *msglen; 318 eob = msg + msgsize; 319 320 /* Name. */ 321 n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr); 322 if (n < 0) 323 return (NS_TSIG_ERROR_NO_SPACE); 324 cp += n; 325 326 /* Type, class, ttl, length (not filled in yet). */ 327 BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ); 328 PUTSHORT(ns_t_tsig, cp); 329 PUTSHORT(ns_c_any, cp); 330 PUTLONG(0, cp); /* TTL */ 331 lenp = cp; 332 cp += 2; 333 334 /* Alg. */ 335 n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); 336 if (n < 0) 337 return (NS_TSIG_ERROR_NO_SPACE); 338 cp += n; 339 340 /* Time. */ 341 BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ); 342 PUTSHORT(0, cp); 343 timesigned = time(NULL); 344 PUTLONG(timesigned, cp); 345 PUTSHORT(NS_TSIG_FUDGE, cp); 346 347 /* 348 * Compute the signature. 349 */ 350 351 /* Digest the time signed and fudge. */ 352 cp2 = buf; 353 PUTSHORT(0, cp2); /* Top 16 bits of time */ 354 PUTLONG(timesigned, cp2); 355 PUTSHORT(NS_TSIG_FUDGE, cp2); 356 357 dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, 358 buf, cp2 - buf, NULL, 0); 359 360 n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, 361 state->sig, sizeof(state->sig)); 362 if (n < 0) 363 return (-ns_r_badkey); 364 state->siglen = n; 365 366 /* Add the signature. */ 367 BOUNDS_CHECK(cp, INT16SZ + state->siglen); 368 PUTSHORT(state->siglen, cp); 369 memcpy(cp, state->sig, state->siglen); 370 cp += state->siglen; 371 372 /* The original message ID & error. */ 373 BOUNDS_CHECK(cp, INT16SZ + INT16SZ); 374 PUTSHORT(ntohs(hp->id), cp); /* already in network order */ 375 PUTSHORT(error, cp); 376 377 /* Other data. */ 378 BOUNDS_CHECK(cp, INT16SZ); 379 PUTSHORT(0, cp); 380 381 /* Go back and fill in the length. */ 382 PUTSHORT(cp - lenp - INT16SZ, lenp); 383 384 hp->arcount = htons(ntohs(hp->arcount) + 1); 385 *msglen = (cp - msg); 386 return (0); 387 } 388