📄 ntp_crypto.c
字号:
if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) == XEVNT_OK) len += crypto_send(fp, &vtemp); value_free(&vtemp); break; /* * Find peer and send autokey data and signature in broadcast * server and symmetric modes. Use the values in the autokey * structure. If no association is found, either the server has * restarted with new associations or some perp has replayed an * old message, in which case light the error bit. */ case CRYPTO_AUTO | CRYPTO_RESP: if ((peer = findpeerbyassoc(associd)) == NULL) { rval = XEVNT_ERR; break; } peer->flags &= ~FLAG_ASSOC; len += crypto_send(fp, &peer->sndval); break; /* * Send leapseconds table and signature. Use the values from the * tai structure. If no table has been loaded, just send an * empty request. */ case CRYPTO_TAI: case CRYPTO_TAI | CRYPTO_RESP: if (crypto_flags & CRYPTO_FLAG_TAI) len += crypto_send(fp, &tai_leap); break; /* * Default - Fall through for requests; for unknown responses, * flag as error. */ default: if (opcode & CRYPTO_RESP) rval = XEVNT_ERR; } /* * In case of error, flame the log. If a request, toss the * puppy; if a response, return so the sender can flame, too. */ if (rval != XEVNT_OK) { opcode |= CRYPTO_ERROR; sprintf(statstr, "error %x opcode %x", rval, opcode); record_crypto_stats(srcadr_sin, statstr); report_event(rval, NULL);#ifdef DEBUG if (debug) printf("crypto_xmit: %s\n", statstr);#endif if (!(opcode & CRYPTO_RESP)) return (0); } /* * Round up the field length to a multiple of 8 bytes and save * the request code and length. */ len = ((len + 7) / 8) * 8; fp->opcode = htonl((opcode & 0xffff0000) | len);#ifdef DEBUG if (debug) printf( "crypto_xmit: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", crypto_flags, start, len, opcode >> 16, associd);#endif return (len);}/* * crypto_verify - parse and verify the extension field and value * * Returns * XEVNT_OK success * XEVNT_LEN bad field format or length * XEVNT_TSP bad timestamp * XEVNT_FSP bad filestamp * XEVNT_PUB bad or missing public key * XEVNT_SGL bad signature length * XEVNT_SIG signature not verified * XEVNT_ERR protocol error */static intcrypto_verify( struct exten *ep, /* extension pointer */ struct value *vp, /* value pointer */ struct peer *peer /* peer structure pointer */ ){ EVP_PKEY *pkey; /* server public key */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp, tstamp1 = 0; /* timestamp */ tstamp_t fstamp, fstamp1 = 0; /* filestamp */ u_int vallen; /* value length */ u_int siglen; /* signature length */ u_int opcode, len; int i; /* * We require valid opcode and field lengths, timestamp, * filestamp, public key, digest, signature length and * signature, where relevant. Note that preliminary length * checks are done in the main loop. */ len = ntohl(ep->opcode) & 0x0000ffff; opcode = ntohl(ep->opcode) & 0xffff0000; /* * Check for valid operation code and protocol. The opcode must * not have the error bit set. If a response, it must have a * value header. If a request and does not contain a value * header, no need for further checking. */ if (opcode & CRYPTO_ERROR) return (XEVNT_ERR); if (opcode & CRYPTO_RESP) { if (len < VALUE_LEN) return (XEVNT_LEN); } else { if (len < VALUE_LEN) return (XEVNT_OK); } /* * We have a value header. Check for valid field lengths. The * field length must be long enough to contain the value header, * value and signature. Note both the value and signature fields * are rounded up to the next word. */ vallen = ntohl(ep->vallen); i = (vallen + 3) / 4; siglen = ntohl(ep->pkt[i++]); if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) / 4) * 4) return (XEVNT_LEN); /* * Punt if this is a response with no data. Punt if this is a * request and a previous response is pending. */ if (opcode & CRYPTO_RESP) { if (vallen == 0) return (XEVNT_LEN); } else { if (peer->cmmd != NULL) return (XEVNT_LEN); } /* * Check for valid timestamp and filestamp. If the timestamp is * zero, the sender is not synchronized and signatures are * disregarded. If not, the timestamp must not precede the * filestamp. The timestamp and filestamp must not precede the * corresponding values in the value structure, if present. Once * the autokey values have been installed, the timestamp must * always be later than the corresponding value in the value * structure. Duplicate timestamps are illegal once the cookie * has been validated. */ tstamp = ntohl(ep->tstamp); fstamp = ntohl(ep->fstamp); if (tstamp == 0) return (XEVNT_OK); if (tstamp < fstamp) return (XEVNT_TSP); if (vp != NULL) { tstamp1 = ntohl(vp->tstamp); fstamp1 = ntohl(vp->fstamp); if ((tstamp < tstamp1 || (tstamp == tstamp1 && (peer->crypto & CRYPTO_FLAG_AUTO)))) return (XEVNT_TSP); if ((tstamp < fstamp1 || fstamp < fstamp1)) return (XEVNT_FSP); } /* * Check for valid signature length, public key and digest * algorithm. */ if (crypto_flags & peer->crypto & CRYPTO_FLAG_PRIV) pkey = sign_pkey; else pkey = peer->pkey; if (siglen == 0 || pkey == NULL || peer->digest == NULL) return (XEVNT_OK); if (siglen != (u_int)EVP_PKEY_size(pkey)) return (XEVNT_SGL); /* * Darn, I thought we would never get here. Verify the * signature. If the identity exchange is verified, light the * proventic bit. If no client identity scheme is specified, * avoid doing the sign exchange. */ EVP_VerifyInit(&ctx, peer->digest); EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12); if (!EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen, pkey)) return (XEVNT_SIG); if (peer->crypto & CRYPTO_FLAG_VRFY) { peer->crypto |= CRYPTO_FLAG_PROV; if (!(crypto_flags & CRYPTO_FLAG_MASK)) peer->crypto |= CRYPTO_FLAG_SIGN; } return (XEVNT_OK);}/* * crypto_encrypt - construct encrypted cookie and signature from * extension field and cookie * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_CKY bad or missing cookie * XEVNT_PER host certificate expired */static intcrypto_encrypt( struct exten *ep, /* extension pointer */ struct value *vp, /* value pointer */ keyid_t *cookie /* server cookie */ ){ EVP_PKEY *pkey; /* public key */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ u_int32 temp32; u_int len; u_char *ptr; /* * Extract the public key from the request. */ len = ntohl(ep->vallen); ptr = (u_char *)ep->pkt; pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_encrypt %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_PUB); } /* * Encrypt the cookie, encode in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = hostval.tstamp; len = EVP_PKEY_size(pkey); vp->vallen = htonl(len); vp->ptr = emalloc(len); temp32 = htonl(*cookie); if (!RSA_public_encrypt(4, (u_char *)&temp32, vp->ptr, pkey->pkey.rsa, RSA_PKCS1_OAEP_PADDING)) { msyslog(LOG_ERR, "crypto_encrypt %s\n", ERR_error_string(ERR_get_error(), NULL)); EVP_PKEY_free(pkey); return (XEVNT_CKY); } EVP_PKEY_free(pkey); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK);}/* * crypto_ident - construct extension field for identity scheme * * This routine determines which identity scheme is in use and * constructs an extension field for that scheme. */u_intcrypto_ident( struct peer *peer /* peer structure pointer */ ){ char filename[MAXFILENAME + 1]; /* * If the server identity has already been verified, no further * action is necessary. Otherwise, try to load the identity file * of the certificate issuer. If the issuer file is not found, * try the host file. If nothing found, declare a cryptobust. * Note we can't get here unless the trusted certificate has * been found and the CRYPTO_FLAG_VALID bit is set, so the * certificate issuer is valid. */ if (peer->ident_pkey != NULL) EVP_PKEY_free(peer->ident_pkey); if (peer->crypto & CRYPTO_FLAG_GQ) { snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_GQ); snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_GQ); } if (peer->crypto & CRYPTO_FLAG_IFF) { snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_IFF); snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_IFF); } if (peer->crypto & CRYPTO_FLAG_MV) { snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_MV); snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_MV); } /* * No compatible identity scheme is available. Life is hard. */ msyslog(LOG_INFO, "crypto_ident: no compatible identity scheme found"); return (0);}/* * crypto_args - construct extension field from arguments * * This routine creates an extension field with current timestamps and * specified opcode, association ID and optional string. Note that the * extension field is created here, but freed after the crypto_xmit() * call in the protocol module. * * Returns extension field pointer (no errors). */struct exten *crypto_args( struct peer *peer, /* peer structure pointer */ u_int opcode, /* operation code */ char *str /* argument string */ ){ tstamp_t tstamp; /* NTP timestamp */ struct exten *ep; /* extension field pointer */ u_int len; /* extension field length */ tstamp = crypto_time(); len = sizeof(struct exten); if (str != NULL) len += strlen(str); ep = emalloc(len); memset(ep, 0, len); if (opcode == 0) return (ep); ep->opcode = htonl(opcode + len); /* * If a response, send our ID; if a request, send the * responder's ID. */ if (opcode & CRYPTO_RESP) ep->associd = htonl(peer->associd); else ep->associd = htonl(peer->assoc); ep->tstamp = htonl(tstamp); ep->fstamp = hostval.tstamp; ep->vallen = 0; if (str != NULL) { ep->vallen = htonl(strlen(str)); memcpy((char *)ep->pkt, str, strlen(str)); } else { ep->pkt[0] = peer->associd; } return (ep);}/* * crypto_send - construct extension field from value components * * Returns extension field length. Note: it is not polite to send a * nonempty signature with zero timestamp or a nonzero timestamp with * empty signature, but these rules are not enforced here. */u_intcrypto_send( struct exten *ep, /* extension field pointer */ struct value *vp /* value pointer */ ){ u_int len, temp32; int i; /* * Copy data. If the data field is empty or zero length, encode * an empty value with length zero. */ ep->tstamp = vp->tstamp; ep->fstamp = vp->fstamp; ep->vallen = vp->vallen; len = 12; temp32 = ntohl(vp->vallen); if (temp32 > 0 && vp->ptr != NULL) memcpy(ep->pkt, vp->ptr, temp32); /* * Copy signature. If the signature field is empty or zero * length, encode an empty signature with length zero. */ i = (temp32 + 3) / 4; len += i * 4 + 4; ep->pkt[i++] = vp->siglen; temp32 = ntohl(vp->siglen); if (temp32 > 0 && vp->sig != NULL) memcpy(&ep->pkt[i], vp->sig, temp32); len += temp32; return (len);}/* * crypto_update - compute new public value and sign extension fields * * This routine runs periodically, like once a day, and when something * changes. It updates the timestamps on three value structures and one * value structure list, then signs all the structures: *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -