crypto_recv(
	struct peer *peer,	/* peer structure pointer */
	struct recvbuf *rbufp	/* packet buffer pointer */
	)
{
	const EVP_MD *dp;	/* message digest algorithm */
	u_int32	*pkt;		/* receive packet pointer */
	struct autokey *ap, *bp; /* autokey pointer */
	struct exten *ep, *fp;	/* extension pointers */
	struct cert_info *xinfo; /* certificate info pointer */
	int	macbytes;	/* length of MAC field, signed by intention */
	int	authlen;	/* offset of MAC field */
	associd_t associd;	/* association ID */
	tstamp_t fstamp = 0;	/* filestamp */
	u_int	len;		/* extension field length */
	u_int	code;		/* extension field opcode */
	u_int	vallen = 0;	/* value length */
	X509	*cert;		/* X509 certificate */
	char	statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
	keyid_t	cookie;		/* crumbles */
	int	hismode;	/* packet mode */
	int	rval = XEVNT_OK;
	const u_char *puch;
	u_int32 temp32;

	/*
	 * Initialize. Note that the packet has already been checked for
	 * valid format and extension field lengths. First extract the
	 * field length, command code and association ID in host byte
	 * order. These are used with all commands and modes. Then check
	 * the version number, which must be 2, and length, which must
	 * be at least 8 for requests and VALUE_LEN (24) for responses.
	 * Packets that fail either test sink without a trace. The
	 * association ID is saved only if nonzero.
	 */
	authlen = LEN_PKT_NOMAC;
	hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode);
	while ((macbytes = rbufp->recv_length - authlen) > (int)MAX_MAC_LEN) {
		/* We can be reasonably sure that we can read at least
		 * the opcode and the size field here. More stringent
		 * checks follow up shortly.
		 */
		pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4;
		ep = (struct exten *)pkt;
		code = ntohl(ep->opcode) & 0xffff0000;
		len = ntohl(ep->opcode) & 0x0000ffff;
		// HMS: Why pkt[1] instead of ep->associd ?
		associd = (associd_t)ntohl(pkt[1]);
		rval = XEVNT_OK;
		DPRINTF(1, ("crypto_recv: flags 0x%x ext offset %d len %u code 0x%x associd %d\n",
			    peer->crypto, authlen, len, code >> 16,
			    associd));

		/*
		 * Check version number and field length. If bad,
		 * quietly ignore the packet.
		 */
		if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8) {
			sys_badlength++;
			code |= CRYPTO_ERROR;
		}

		/* Check if the declared size fits into the remaining
		 * buffer.
		 */
		if (len > macbytes) {
			DPRINTF(1, ("crypto_recv: possible attack detected, associd %d\n",
				    associd));
			return XEVNT_LEN;
		}

		/* Check if the paylod of the extension fits into the
		 * declared frame.
		 */
		if (len >= VALUE_LEN) {
			fstamp = ntohl(ep->fstamp);
			vallen = ntohl(ep->vallen);
			/*
			 * Bug 2761: I hope this isn't too early...
			 */
			if (   vallen == 0
			    || len - VALUE_LEN < vallen)
				return XEVNT_LEN;
		}
		switch (code) {

		/*
		 * Install status word, host name, signature scheme and
		 * association ID. In OpenSSL the signature algorithm is
		 * bound to the digest algorithm, so the NID completely
		 * defines the signature scheme. Note the request and
		 * response are identical, but neither is validated by
		 * signature. The request is processed here only in
		 * symmetric modes. The server name field might be
		 * useful to implement access controls in future.
		 */
		case CRYPTO_ASSOC:

			/*
			 * If our state machine is running when this
			 * message arrives, the other fellow might have
			 * restarted. However, this could be an
			 * intruder, so just clamp the poll interval and
			 * find out for ourselves. Otherwise, pass the
			 * extension field to the transmit side.
			 */
			if (peer->crypto & CRYPTO_FLAG_CERT) {
				rval = XEVNT_ERR;
				break;
			}
			if (peer->cmmd) {
				if (peer->assoc != associd) {
					rval = XEVNT_ERR;
					break;
				}
			}
			fp = emalloc(len);
			memcpy(fp, ep, len);
			fp->associd = htonl(peer->associd);
			peer->cmmd = fp;
			/* fall through */

		case CRYPTO_ASSOC | CRYPTO_RESP:

			/*
			 * Discard the message if it has already been
			 * stored or the message has been amputated.
			 */
			if (peer->crypto) {
				if (peer->assoc != associd)
					rval = XEVNT_ERR;
				break;
			}
			INSIST(len >= VALUE_LEN);
			if (vallen == 0 || vallen > MAXHOSTNAME ||
			    len - VALUE_LEN < vallen) {
				rval = XEVNT_LEN;
				break;
			}
			DPRINTF(1, ("crypto_recv: ident host 0x%x %d server 0x%x %d\n",
				    crypto_flags, peer->associd, fstamp,
				    peer->assoc));
			temp32 = crypto_flags & CRYPTO_FLAG_MASK;

			/*
			 * If the client scheme is PC, the server scheme
			 * must be PC. The public key and identity are
			 * presumed valid, so we skip the certificate
			 * and identity exchanges and move immediately
			 * to the cookie exchange which confirms the
			 * server signature.
			 */
			if (crypto_flags & CRYPTO_FLAG_PRIV) {
				if (!(fstamp & CRYPTO_FLAG_PRIV)) {
					rval = XEVNT_KEY;
					break;
				}
				fstamp |= CRYPTO_FLAG_CERT |
				    CRYPTO_FLAG_VRFY | CRYPTO_FLAG_SIGN;

			/*
			 * It is an error if either peer supports
			 * identity, but the other does not.
			 */
			} else if (hismode == MODE_ACTIVE || hismode ==
			    MODE_PASSIVE) {
				if ((temp32 && !(fstamp &
				    CRYPTO_FLAG_MASK)) ||
				    (!temp32 && (fstamp &
				    CRYPTO_FLAG_MASK))) {
					rval = XEVNT_KEY;
					break;
				}
			}

			/*
			 * Discard the message if the signature digest
			 * NID is not supported.
			 */
			temp32 = (fstamp >> 16) & 0xffff;
			dp =
			    (const EVP_MD *)EVP_get_digestbynid(temp32);
			if (dp == NULL) {
				rval = XEVNT_MD;
				break;
			}

			/*
			 * Save status word, host name and message
			 * digest/signature type. If this is from a
			 * broadcast and the association ID has changed,
			 * request the autokey values.
			 */
			peer->assoc = associd;
			if (hismode == MODE_SERVER)
				fstamp |= CRYPTO_FLAG_AUTO;
			if (!(fstamp & CRYPTO_FLAG_TAI))
				fstamp |= CRYPTO_FLAG_LEAP;
			RAND_bytes((u_char *)&peer->hcookie, 4);
			peer->crypto = fstamp;
			peer->digest = dp;
			if (peer->subject != NULL)
				free(peer->subject);
			peer->subject = emalloc(vallen + 1);
			memcpy(peer->subject, ep->pkt, vallen);
			peer->subject[vallen] = '\0';
			if (peer->issuer != NULL)
				free(peer->issuer);
			peer->issuer = estrdup(peer->subject);
			snprintf(statstr, sizeof(statstr),
			    "assoc %d %d host %s %s", peer->associd,
			    peer->assoc, peer->subject,
			    OBJ_nid2ln(temp32));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * Decode X509 certificate in ASN.1 format and extract
		 * the data containing, among other things, subject
		 * name and public key. In the default identification
		 * scheme, the certificate trail is followed to a self
		 * signed trusted certificate.
		 */
		case CRYPTO_CERT | CRYPTO_RESP:

			/*
			 * Discard the message if empty or invalid.
			 */
			if (len < VALUE_LEN)
				break;

			if ((rval = crypto_verify(ep, NULL, peer)) !=
			    XEVNT_OK)
				break;

			/*
			 * Scan the certificate list to delete old
			 * versions and link the newest version first on
			 * the list. Then, verify the signature. If the
			 * certificate is bad or missing, just ignore
			 * it.
			 */
			if ((xinfo = cert_install(ep, peer)) == NULL) {
				rval = XEVNT_CRT;
				break;
			}
			if ((rval = cert_hike(peer, xinfo)) != XEVNT_OK)
				break;

			/*
			 * We plug in the public key and lifetime from
			 * the first certificate received. However, note
			 * that this certificate might not be signed by
			 * the server, so we can't check the
			 * signature/digest NID.
			 */
			if (peer->pkey == NULL) {
				puch = xinfo->cert.ptr;
				cert = d2i_X509(NULL, &puch,
				    ntohl(xinfo->cert.vallen));
				peer->pkey = X509_get_pubkey(cert);
				X509_free(cert);
			}
			peer->flash &= ~TEST8;
			temp32 = xinfo->nid;
			snprintf(statstr, sizeof(statstr),
			    "cert %s %s 0x%x %s (%u) fs %u",
			    xinfo->subject, xinfo->issuer, xinfo->flags,
			    OBJ_nid2ln(temp32), temp32,
			    ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * Schnorr (IFF) identity scheme. This scheme is
		 * designed for use with shared secret server group keys
		 * and where the certificate may be generated by a third
		 * party. The client sends a challenge to the server,
		 * which performs a calculation and returns the result.
		 * A positive result is possible only if both client and
		 * server contain the same secret group key.
		 */
		case CRYPTO_IFF | CRYPTO_RESP:

			/*
			 * Discard the message if invalid.
			 */
			if ((rval = crypto_verify(ep, NULL, peer)) !=
			    XEVNT_OK)
				break;

			/*
			 * If the challenge matches the response, the
			 * server public key, signature and identity are
			 * all verified at the same time. The server is
			 * declared trusted, so we skip further
			 * certificate exchanges and move immediately to
			 * the cookie exchange.
			 */
			if ((rval = crypto_iff(ep, peer)) != XEVNT_OK)
				break;

			peer->crypto |= CRYPTO_FLAG_VRFY;
			peer->flash &= ~TEST8;
			snprintf(statstr, sizeof(statstr), "iff %s fs %u",
			    peer->issuer, ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * Guillou-Quisquater (GQ) identity scheme. This scheme
		 * is designed for use with public certificates carrying
		 * the GQ public key in an extension field. The client
		 * sends a challenge to the server, which performs a
		 * calculation and returns the result. A positive result
		 * is possible only if both client and server contain
		 * the same group key and the server has the matching GQ
		 * private key.
		 */
		case CRYPTO_GQ | CRYPTO_RESP:

			/*
			 * Discard the message if invalid
			 */
			if ((rval = crypto_verify(ep, NULL, peer)) !=
			    XEVNT_OK)
				break;

			/*
			 * If the challenge matches the response, the
			 * server public key, signature and identity are
			 * all verified at the same time. The server is
			 * declared trusted, so we skip further
			 * certificate exchanges and move immediately to
			 * the cookie exchange.
			 */
			if ((rval = crypto_gq(ep, peer)) != XEVNT_OK)
				break;

			peer->crypto |= CRYPTO_FLAG_VRFY;
			peer->flash &= ~TEST8;
			snprintf(statstr, sizeof(statstr), "gq %s fs %u",
			    peer->issuer, ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * Mu-Varadharajan (MV) identity scheme. This scheme is
		 * designed for use with three levels of trust, trusted
		 * host, server and client. The trusted host key is
		 * opaque to servers and clients; the server keys are
		 * opaque to clients and each client key is different.
		 * Client keys can be revoked without requiring new key
		 * generations.
		 */
		case CRYPTO_MV | CRYPTO_RESP:

			/*
			 * Discard the message if invalid.
			 */
			if ((rval = crypto_verify(ep, NULL, peer)) !=
			    XEVNT_OK)
				break;

			/*
			 * If the challenge matches the response, the
			 * server public key, signature and identity are
			 * all verified at the same time. The server is
			 * declared trusted, so we skip further
			 * certificate exchanges and move immediately to
			 * the cookie exchange.
			 */
			if ((rval = crypto_mv(ep, peer)) != XEVNT_OK)
				break;

			peer->crypto |= CRYPTO_FLAG_VRFY;
			peer->flash &= ~TEST8;
			snprintf(statstr, sizeof(statstr), "mv %s fs %u",
			    peer->issuer, ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * Cookie response in client and symmetric modes. If the
		 * cookie bit is set, the working cookie is the EXOR of
		 * the current and new values.
		 */
		case CRYPTO_COOK | CRYPTO_RESP:

			/*
			 * Discard the message if invalid or signature
			 * not verified with respect to the cookie
			 * values.
			 */
			if ((rval = crypto_verify(ep, &peer->cookval,
			    peer)) != XEVNT_OK)
				break;

			/*
			 * Decrypt the cookie, hunting all the time for
			 * errors.
			 */
			if (vallen == (u_int)EVP_PKEY_size(host_pkey)) {
				u_int32 *cookiebuf = malloc(
				    RSA_size(host_pkey->pkey.rsa));
				if (!cookiebuf) {
					rval = XEVNT_CKY;
					break;
				}

				if (RSA_private_decrypt(vallen,
				    (u_char *)ep->pkt,
				    (u_char *)cookiebuf,
				    host_pkey->pkey.rsa,
				    RSA_PKCS1_OAEP_PADDING) != 4) {
					rval = XEVNT_CKY;
					free(cookiebuf);
					break;
				} else {
					cookie = ntohl(*cookiebuf);
					free(cookiebuf);
				}
			} else {
				rval = XEVNT_CKY;
				break;
			}

			/*
			 * Install cookie values and light the cookie
			 * bit. If this is not broadcast client mode, we
			 * are done here.
			 */
			key_expire(peer);
			if (hismode == MODE_ACTIVE || hismode ==
			    MODE_PASSIVE)
				peer->pcookie = peer->hcookie ^ cookie;
			else
				peer->pcookie = cookie;
			peer->crypto |= CRYPTO_FLAG_COOK;
			peer->flash &= ~TEST8;
			snprintf(statstr, sizeof(statstr),
			    "cook %x ts %u fs %u", peer->pcookie,
			    ntohl(ep->tstamp), ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * Install autokey values in broadcast client and
		 * symmetric modes. We have to do this every time the
		 * sever/peer cookie changes or a new keylist is
		 * rolled. Ordinarily, this is automatic as this message
		 * is piggybacked on the first NTP packet sent upon
		 * either of these events. Note that a broadcast client
		 * or symmetric peer can receive this response without a
		 * matching request.
		 */
		case CRYPTO_AUTO | CRYPTO_RESP:

			/*
			 * Discard the message if invalid or signature
			 * not verified with respect to the receive
			 * autokey values.
			 */
			if ((rval = crypto_verify(ep, &peer->recval,
			    peer)) != XEVNT_OK)
				break;

			/*
			 * Discard the message if a broadcast client and
			 * the association ID does not match. This might
			 * happen if a broacast server restarts the
			 * protocol. A protocol restart will occur at
			 * the next ASSOC message.
			 */
			if ((peer->cast_flags & MDF_BCLNT) &&
			    peer->assoc != associd)
				break;

			/*
			 * Install autokey values and light the
			 * autokey bit. This is not hard.
			 */
			if (ep->tstamp == 0)
				break;

			if (peer->recval.ptr == NULL)
				peer->recval.ptr =
				    emalloc(sizeof(struct autokey));
			bp = (struct autokey *)peer->recval.ptr;
			peer->recval.tstamp = ep->tstamp;
			peer->recval.fstamp = ep->fstamp;
			ap = (struct autokey *)ep->pkt;
			bp->seq = ntohl(ap->seq);
			bp->key = ntohl(ap->key);
			peer->pkeyid = bp->key;
			peer->crypto |= CRYPTO_FLAG_AUTO;
			peer->flash &= ~TEST8;
			snprintf(statstr, sizeof(statstr),
			    "auto seq %d key %x ts %u fs %u", bp->seq,
			    bp->key, ntohl(ep->tstamp),
			    ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * X509 certificate sign response. Validate the
		 * certificate signed by the server and install. Later
		 * this can be provided to clients of this server in
		 * lieu of the self signed certificate in order to
		 * validate the public key.
		 */
		case CRYPTO_SIGN | CRYPTO_RESP:

			/*
			 * Discard the message if invalid.
			 */
			if ((rval = crypto_verify(ep, NULL, peer)) !=
			    XEVNT_OK)
				break;

			/*
			 * Scan the certificate list to delete old
			 * versions and link the newest version first on
			 * the list.
			 */
			if ((xinfo = cert_install(ep, peer)) == NULL) {
				rval = XEVNT_CRT;
				break;
			}
			peer->crypto |= CRYPTO_FLAG_SIGN;
			peer->flash &= ~TEST8;
			temp32 = xinfo->nid;
			snprintf(statstr, sizeof(statstr),
			    "sign %s %s 0x%x %s (%u) fs %u",
			    xinfo->subject, xinfo->issuer, xinfo->flags,
			    OBJ_nid2ln(temp32), temp32,
			    ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * Install leapseconds values. While the leapsecond
		 * values epoch, TAI offset and values expiration epoch
		 * are retained, only the current TAI offset is provided
		 * via the kernel to other applications.
		 */
		case CRYPTO_LEAP | CRYPTO_RESP:
			/*
			 * Discard the message if invalid. We can't
			 * compare the value timestamps here, as they
			 * can be updated by different servers.
			 */
			rval = crypto_verify(ep, NULL, peer);
			if ((rval   != XEVNT_OK          ) ||
			    (vallen != 3*sizeof(uint32_t))  )
				break;

			/* Check if we can update the basic TAI offset
			 * for our current leap frame. This is a hack
			 * and ignores the time stamps in the autokey
			 * message.
			 */
			if (sys_leap != LEAP_NOTINSYNC)
				leapsec_autokey_tai(ntohl(ep->pkt[0]),
						    rbufp->recv_time.l_ui, NULL);
			tai_leap.tstamp = ep->tstamp;
			tai_leap.fstamp = ep->fstamp;
			crypto_update();
			mprintf_event(EVNT_TAI, peer,
				      "%d seconds", ntohl(ep->pkt[0]));
			peer->crypto |= CRYPTO_FLAG_LEAP;
			peer->flash &= ~TEST8;
			snprintf(statstr, sizeof(statstr),
				 "leap TAI offset %d at %u expire %u fs %u",
				 ntohl(ep->pkt[0]), ntohl(ep->pkt[1]),
				 ntohl(ep->pkt[2]), ntohl(ep->fstamp));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			break;

		/*
		 * We come here in symmetric modes for miscellaneous
		 * commands that have value fields but are processed on
		 * the transmit side. All we need do here is check for
		 * valid field length. Note that ASSOC is handled
		 * separately.
		 */
		case CRYPTO_CERT:
		case CRYPTO_IFF:
		case CRYPTO_GQ:
		case CRYPTO_MV:
		case CRYPTO_COOK:
		case CRYPTO_SIGN:
			if (len < VALUE_LEN) {
				rval = XEVNT_LEN;
				break;
			}
			/* fall through */

		/*
		 * We come here in symmetric modes for requests
		 * requiring a response (above plus AUTO and LEAP) and
		 * for responses. If a request, save the extension field
		 * for later; invalid requests will be caught on the
		 * transmit side. If an error or invalid response,
		 * declare a protocol error.
		 */
		default:
			if (code & (CRYPTO_RESP | CRYPTO_ERROR)) {
				rval = XEVNT_ERR;
			} else if (peer->cmmd == NULL) {
				fp = emalloc(len);
				memcpy(fp, ep, len);
				peer->cmmd = fp;
			}
		}

		/*
		 * The first error found terminates the extension field
		 * scan and we return the laundry to the caller.
		 */
		if (rval != XEVNT_OK) {
			snprintf(statstr, sizeof(statstr),
			    "%04x %d %02x %s", htonl(ep->opcode),
			    associd, rval, eventstr(rval));
			record_crypto_stats(&peer->srcadr, statstr);
			DPRINTF(1, ("crypto_recv: %s\n", statstr));
			return (rval);
		}
		authlen += (len + 3) / 4 * 4;
	}
	return (rval);
}


crypto_xmit(
	struct peer *peer,	/* peer structure pointer */
	struct pkt *xpkt,	/* transmit packet pointer */
	struct recvbuf *rbufp,	/* receive buffer pointer */
	int	start,		/* offset to extension field */
	struct exten *ep,	/* extension pointer */
	keyid_t cookie		/* session cookie */
	)
{
	struct exten *fp;	/* extension pointers */
	struct cert_info *cp, *xp, *yp; /* cert info/value pointer */
	sockaddr_u *srcadr_sin; /* source address */
	u_int32	*pkt;		/* packet pointer */
	u_int	opcode;		/* extension field opcode */
	char	certname[MAXHOSTNAME + 1]; /* subject name buffer */
	char	statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
	tstamp_t tstamp;
	struct calendar tscal;
	u_int	vallen;
	struct value vtemp;
	associd_t associd;
	int	rval;
	int	len;
	keyid_t tcookie;

	/*
	 * Generate the requested extension field request code, length
	 * and association ID. If this is a response and the host is not
	 * synchronized, light the error bit and go home.
	 */
	pkt = (u_int32 *)xpkt + start / 4;
	fp = (struct exten *)pkt;
	opcode = ntohl(ep->opcode);
	if (peer != NULL) {
		srcadr_sin = &peer->srcadr;
		if (!(opcode & CRYPTO_RESP))
			peer->opcode = ep->opcode;
	} else {
		srcadr_sin = &rbufp->recv_srcadr;
	}
	associd = (associd_t) ntohl(ep->associd);
	len = 8;
	fp->opcode = htonl((opcode & 0xffff0000) | len);
	fp->associd = ep->associd;
	rval = XEVNT_OK;
	tstamp = crypto_time();
	switch (opcode & 0xffff0000) {

	/*
	 * Send association request and response with status word and
	 * host name. Note, this message is not signed and the filestamp
	 * contains only the status word.
	 */
	case CRYPTO_ASSOC:
	case CRYPTO_ASSOC | CRYPTO_RESP:
		len = crypto_send(fp, &hostval, start);
		fp->fstamp = htonl(crypto_flags);
		break;

	/*
	 * Send certificate request. Use the values from the extension
	 * field.
	 */
	case CRYPTO_CERT:
		memset(&vtemp, 0, sizeof(vtemp));
		vtemp.tstamp = ep->tstamp;
		vtemp.fstamp = ep->fstamp;
		vtemp.vallen = ep->vallen;
		vtemp.ptr = (u_char *)ep->pkt;
		len = crypto_send(fp, &vtemp, start);
		break;

	/*
	 * Send sign request. Use the host certificate, which is self-
	 * signed and may or may not be trusted.
	 */
	case CRYPTO_SIGN:
		(void)ntpcal_ntp_to_date(&tscal, tstamp, NULL);
		if ((calcomp(&tscal, &(cert_host->first)) < 0)
		|| (calcomp(&tscal, &(cert_host->last)) > 0))
			rval = XEVNT_PER;
		else
			len = crypto_send(fp, &cert_host->cert, start);
		break;

	/*
	 * Send certificate response. Use the name in the extension
	 * field to find the certificate in the cache. If the request
	 * contains no subject name, assume the name of this host. This
	 * is for backwards compatibility. Private certificates are
	 * never sent.
	 *
	 * There may be several certificates matching the request. First
	 * choice is a self-signed trusted certificate; second choice is
	 * any certificate signed by another host. There is no third
	 * choice.
	 */
	case CRYPTO_CERT | CRYPTO_RESP:
		vallen = ntohl(ep->vallen);	/* Must be <64k */
		if (vallen == 0 || vallen > MAXHOSTNAME ||
		    len - VALUE_LEN < vallen) {
			rval = XEVNT_LEN;
			break;
		}

		/*
		 * Find all public valid certificates with matching
		 * subject. If a self-signed, trusted certificate is
		 * found, use that certificate. If not, use the last non
		 * self-signed certificate.
		 */
		memcpy(certname, ep->pkt, vallen);
		certname[vallen] = '\0';
		xp = yp = NULL;
		for (cp = cinfo; cp != NULL; cp = cp->link) {
			if (cp->flags & (CERT_PRIV | CERT_ERROR))
				continue;

			if (strcmp(certname, cp->subject) != 0)
				continue;

			if (strcmp(certname, cp->issuer) != 0)
				yp = cp;
			else if (cp ->flags & CERT_TRUST)
				xp = cp;
			continue;
		}

		/*
		 * Be careful who you trust. If the certificate is not
		 * found, return an empty response. Note that we dont
		 * enforce lifetimes here.
		 *
		 * The timestamp and filestamp are taken from the
		 * certificate value structure. For all certificates the
		 * timestamp is the latest signature update time. For
		 * host and imported certificates the filestamp is the
		 * creation epoch. For signed certificates the filestamp
		 * is the creation epoch of the trusted certificate at
		 * the root of the certificate trail. In principle, this
		 * allows strong checking for signature masquerade.
		 */
		if (xp == NULL)
			xp = yp;
		if (xp == NULL)
			break;

		if (tstamp == 0)
			break;

		len = crypto_send(fp, &xp->cert, start);
		break;

	/*
	 * Send challenge in Schnorr (IFF) identity scheme.
	 */
	case CRYPTO_IFF:
		if (peer == NULL)
			break;		/* hack attack */

		if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send response in Schnorr (IFF) identity scheme.
	 */
	case CRYPTO_IFF | CRYPTO_RESP:
		if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send challenge in Guillou-Quisquater (GQ) identity scheme.
	 */
	case CRYPTO_GQ:
		if (peer == NULL)
			break;		/* hack attack */

		if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send response in Guillou-Quisquater (GQ) identity scheme.
	 */
	case CRYPTO_GQ | CRYPTO_RESP:
		if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send challenge in MV identity scheme.
	 */
	case CRYPTO_MV:
		if (peer == NULL)
			break;		/* hack attack */

		if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send response in MV identity scheme.
	 */
	case CRYPTO_MV | CRYPTO_RESP:
		if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send certificate sign response. The integrity of the request
	 * certificate has already been verified on the receive side.
	 * Sign the response using the local server key. Use the
	 * filestamp from the request and use the timestamp as the
	 * current time. Light the error bit if the certificate is
	 * invalid or contains an unverified signature.
	 */
	case CRYPTO_SIGN | CRYPTO_RESP:
		if ((rval = cert_sign(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send public key and signature. Use the values from the public
	 * key.
	 */
	case CRYPTO_COOK:
		len = crypto_send(fp, &pubkey, start);
		break;

	/*
	 * Encrypt and send cookie and signature. Light the error bit if
	 * anything goes wrong.
	 */
	case CRYPTO_COOK | CRYPTO_RESP:
		vallen = ntohl(ep->vallen);	/* Must be <64k */
		if (   vallen == 0
		    || (vallen >= MAX_VALLEN)
		    || (opcode & 0x0000ffff)  < VALUE_LEN + vallen) {
			rval = XEVNT_LEN;
			break;
		}
		if (peer == NULL)
			tcookie = cookie;
		else
			tcookie = peer->hcookie;
		if ((rval = crypto_encrypt((const u_char *)ep->pkt, vallen, &tcookie, &vtemp))
		    == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			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 == NULL) {
			if ((peer = findpeerbyassoc(associd)) == NULL) {
				rval = XEVNT_ERR;
				break;
			}
		}
		peer->flags &= ~FLAG_ASSOC;
		len = crypto_send(fp, &peer->sndval, start);
		break;

	/*
	 * Send leapseconds values and signature. Use the values from
	 * the tai structure. If no table has been loaded, just send an
	 * empty request.
	 */
	case CRYPTO_LEAP | CRYPTO_RESP:
		len = crypto_send(fp, &tai_leap, start);
		break;

	/*
	 * Default - Send a valid command for unknown requests; send
	 * an error response for unknown resonses.
	 */
	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) {
		u_int32	uint32;

		uint32 = CRYPTO_ERROR;
		opcode |= uint32;
		fp->opcode |= htonl(uint32);
		snprintf(statstr, sizeof(statstr),
		    "%04x %d %02x %s", opcode, associd, rval,
		    eventstr(rval));
		record_crypto_stats(srcadr_sin, statstr);
		DPRINTF(1, ("crypto_xmit: %s\n", statstr));
		if (!(opcode & CRYPTO_RESP))
			return (0);
	}
	DPRINTF(1, ("crypto_xmit: flags 0x%x offset %d len %d code 0x%x associd %d\n",
		    crypto_flags, start, len, opcode >> 16, associd));
	return (len);
}

The crypto_xmit function in ntpd in NTP 4.2.x before 4.2.8p4, and 4.3.x before 4.3.77 allows remote attackers to cause a denial of service (crash) via crafted packets containing particular autokey operations.  NOTE: This vulnerability exists due to an incomplete fix for CVE-2014-9750.


crypto_xmit(
	struct peer *peer,	/* peer structure pointer */
	struct pkt *xpkt,	/* transmit packet pointer */
	struct recvbuf *rbufp,	/* receive buffer pointer */
	int	start,		/* offset to extension field */
	struct exten *ep,	/* extension pointer */
	keyid_t cookie		/* session cookie */
	)
{
	struct exten *fp;	/* extension pointers */
	struct cert_info *cp, *xp, *yp; /* cert info/value pointer */
	sockaddr_u *srcadr_sin; /* source address */
	u_int32	*pkt;		/* packet pointer */
	u_int	opcode;		/* extension field opcode */
	char	certname[MAXHOSTNAME + 1]; /* subject name buffer */
	char	statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
	tstamp_t tstamp;
	struct calendar tscal;
	u_int	vallen;
	struct value vtemp;
	associd_t associd;
	int	rval;
	int	len;
	keyid_t tcookie;

	/*
	 * Generate the requested extension field request code, length
	 * and association ID. If this is a response and the host is not
	 * synchronized, light the error bit and go home.
	 */
	pkt = (u_int32 *)xpkt + start / 4;
	fp = (struct exten *)pkt;
	opcode = ntohl(ep->opcode);
	if (peer != NULL) {
		srcadr_sin = &peer->srcadr;
		if (!(opcode & CRYPTO_RESP))
			peer->opcode = ep->opcode;
	} else {
		srcadr_sin = &rbufp->recv_srcadr;
	}
	associd = (associd_t) ntohl(ep->associd);
	len = 8;
	fp->opcode = htonl((opcode & 0xffff0000) | len);
	fp->associd = ep->associd;
	rval = XEVNT_OK;
	tstamp = crypto_time();
	switch (opcode & 0xffff0000) {

	/*
	 * Send association request and response with status word and
	 * host name. Note, this message is not signed and the filestamp
	 * contains only the status word.
	 */
	case CRYPTO_ASSOC:
	case CRYPTO_ASSOC | CRYPTO_RESP:
		len = crypto_send(fp, &hostval, start);
		fp->fstamp = htonl(crypto_flags);
		break;

	/*
	 * Send certificate request. Use the values from the extension
	 * field.
	 */
	case CRYPTO_CERT:
		memset(&vtemp, 0, sizeof(vtemp));
		vtemp.tstamp = ep->tstamp;
		vtemp.fstamp = ep->fstamp;
		vtemp.vallen = ep->vallen;
		vtemp.ptr = (u_char *)ep->pkt;
		len = crypto_send(fp, &vtemp, start);
		break;

	/*
	 * Send sign request. Use the host certificate, which is self-
	 * signed and may or may not be trusted.
	 */
	case CRYPTO_SIGN:
		(void)ntpcal_ntp_to_date(&tscal, tstamp, NULL);
		if ((calcomp(&tscal, &(cert_host->first)) < 0)
		|| (calcomp(&tscal, &(cert_host->last)) > 0))
			rval = XEVNT_PER;
		else
			len = crypto_send(fp, &cert_host->cert, start);
		break;

	/*
	 * Send certificate response. Use the name in the extension
	 * field to find the certificate in the cache. If the request
	 * contains no subject name, assume the name of this host. This
	 * is for backwards compatibility. Private certificates are
	 * never sent.
	 *
	 * There may be several certificates matching the request. First
	 * choice is a self-signed trusted certificate; second choice is
	 * any certificate signed by another host. There is no third
	 * choice.
	 */
	case CRYPTO_CERT | CRYPTO_RESP:
		vallen = exten_payload_size(ep); /* Must be <64k */
		if (vallen == 0 || vallen >= sizeof(certname) ) {
			rval = XEVNT_LEN;
			break;
		}

		/*
		 * Find all public valid certificates with matching
		 * subject. If a self-signed, trusted certificate is
		 * found, use that certificate. If not, use the last non
		 * self-signed certificate.
		 */
		memcpy(certname, ep->pkt, vallen);
		certname[vallen] = '\0';
		xp = yp = NULL;
		for (cp = cinfo; cp != NULL; cp = cp->link) {
			if (cp->flags & (CERT_PRIV | CERT_ERROR))
				continue;

			if (strcmp(certname, cp->subject) != 0)
				continue;

			if (strcmp(certname, cp->issuer) != 0)
				yp = cp;
			else if (cp ->flags & CERT_TRUST)
				xp = cp;
			continue;
		}

		/*
		 * Be careful who you trust. If the certificate is not
		 * found, return an empty response. Note that we dont
		 * enforce lifetimes here.
		 *
		 * The timestamp and filestamp are taken from the
		 * certificate value structure. For all certificates the
		 * timestamp is the latest signature update time. For
		 * host and imported certificates the filestamp is the
		 * creation epoch. For signed certificates the filestamp
		 * is the creation epoch of the trusted certificate at
		 * the root of the certificate trail. In principle, this
		 * allows strong checking for signature masquerade.
		 */
		if (xp == NULL)
			xp = yp;
		if (xp == NULL)
			break;

		if (tstamp == 0)
			break;

		len = crypto_send(fp, &xp->cert, start);
		break;

	/*
	 * Send challenge in Schnorr (IFF) identity scheme.
	 */
	case CRYPTO_IFF:
		if (peer == NULL)
			break;		/* hack attack */

		if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send response in Schnorr (IFF) identity scheme.
	 */
	case CRYPTO_IFF | CRYPTO_RESP:
		if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send challenge in Guillou-Quisquater (GQ) identity scheme.
	 */
	case CRYPTO_GQ:
		if (peer == NULL)
			break;		/* hack attack */

		if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send response in Guillou-Quisquater (GQ) identity scheme.
	 */
	case CRYPTO_GQ | CRYPTO_RESP:
		if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send challenge in MV identity scheme.
	 */
	case CRYPTO_MV:
		if (peer == NULL)
			break;		/* hack attack */

		if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send response in MV identity scheme.
	 */
	case CRYPTO_MV | CRYPTO_RESP:
		if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send certificate sign response. The integrity of the request
	 * certificate has already been verified on the receive side.
	 * Sign the response using the local server key. Use the
	 * filestamp from the request and use the timestamp as the
	 * current time. Light the error bit if the certificate is
	 * invalid or contains an unverified signature.
	 */
	case CRYPTO_SIGN | CRYPTO_RESP:
		if ((rval = cert_sign(ep, &vtemp)) == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			value_free(&vtemp);
		}
		break;

	/*
	 * Send public key and signature. Use the values from the public
	 * key.
	 */
	case CRYPTO_COOK:
		len = crypto_send(fp, &pubkey, start);
		break;

	/*
	 * Encrypt and send cookie and signature. Light the error bit if
	 * anything goes wrong.
	 */
	case CRYPTO_COOK | CRYPTO_RESP:
		vallen = ntohl(ep->vallen);	/* Must be <64k */
		if (   vallen == 0
		    || (vallen >= MAX_VALLEN)
		    || (opcode & 0x0000ffff)  < VALUE_LEN + vallen) {
			rval = XEVNT_LEN;
			break;
		}
		if (peer == NULL)
			tcookie = cookie;
		else
			tcookie = peer->hcookie;
		if ((rval = crypto_encrypt((const u_char *)ep->pkt, vallen, &tcookie, &vtemp))
		    == XEVNT_OK) {
			len = crypto_send(fp, &vtemp, start);
			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 == NULL) {
			if ((peer = findpeerbyassoc(associd)) == NULL) {
				rval = XEVNT_ERR;
				break;
			}
		}
		peer->flags &= ~FLAG_ASSOC;
		len = crypto_send(fp, &peer->sndval, start);
		break;

	/*
	 * Send leapseconds values and signature. Use the values from
	 * the tai structure. If no table has been loaded, just send an
	 * empty request.
	 */
	case CRYPTO_LEAP | CRYPTO_RESP:
		len = crypto_send(fp, &tai_leap, start);
		break;

	/*
	 * Default - Send a valid command for unknown requests; send
	 * an error response for unknown resonses.
	 */
	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) {
		u_int32	uint32;

		uint32 = CRYPTO_ERROR;
		opcode |= uint32;
		fp->opcode |= htonl(uint32);
		snprintf(statstr, sizeof(statstr),
		    "%04x %d %02x %s", opcode, associd, rval,
		    eventstr(rval));
		record_crypto_stats(srcadr_sin, statstr);
		DPRINTF(1, ("crypto_xmit: %s\n", statstr));
		if (!(opcode & CRYPTO_RESP))
			return (0);
	}
	DPRINTF(1, ("crypto_xmit: flags 0x%x offset %d len %d code 0x%x associd %d\n",
		    crypto_flags, start, len, opcode >> 16, associd));
	return (len);
}


FontInfoScanner::scanFonts Fix infinite loop in broken files

Fixes #752

void FontInfoScanner::scanFonts(XRef *xrefA, Dict *resDict, std::vector<FontInfo*> *fontsList) {
  GfxFontDict *gfxFontDict;
  GfxFont *font;

  // scan the fonts in this resource dictionary
  gfxFontDict = nullptr;
  const Object &fontObj = resDict->lookupNF("Font");
  if (fontObj.isRef()) {
    Object obj2 = fontObj.fetch(xrefA);
    if (obj2.isDict()) {
      Ref r = fontObj.getRef();
      gfxFontDict = new GfxFontDict(xrefA, &r, obj2.getDict());
    }
  } else if (fontObj.isDict()) {
    gfxFontDict = new GfxFontDict(xrefA, nullptr, fontObj.getDict());
  }
  if (gfxFontDict) {
    for (int i = 0; i < gfxFontDict->getNumFonts(); ++i) {
      if ((font = gfxFontDict->getFont(i))) {
        Ref fontRef = *font->getID();

        // add this font to the list if not already found
        if (fonts.find(fontRef.num) == fonts.end()) {
	  fontsList->push_back(new FontInfo(font, xrefA));
          fonts.insert(fontRef.num);
        }
      }
    }
    delete gfxFontDict;
  }

  // recursively scan any resource dictionaries in objects in this
  // resource dictionary
  const char *resTypes[] = { "XObject", "Pattern" };
  for (unsigned int resType = 0; resType < sizeof(resTypes) / sizeof(resTypes[0]); ++resType) {
    Object objDict = resDict->lookup(resTypes[resType]);
    if (objDict.isDict()) {
      for (int i = 0; i < objDict.dictGetLength(); ++i) {
        const Object &dictObjI = objDict.dictGetValNF(i);
        if (dictObjI.isRef()) {
          // check for an already-seen object
          const Ref r = dictObjI.getRef();
          if (visitedObjects.find(r.num) != visitedObjects.end()) {
            continue;
          }

          visitedObjects.insert(r.num);
        }

        Object obj2 = dictObjI.fetch(xrefA);
        if (obj2.isStream()) {
          Object resObj = obj2.streamGetDict()->lookup("Resources");
          if (resObj.isDict() && resObj.getDict() != resDict) {
            scanFonts(xrefA, resObj.getDict(), fontsList);
          }
        }
      }
    }
  }
}

FontInfoScanner::scanFonts in FontInfo.cc in Poppler 0.75.0 has infinite recursion, leading to a call to the error function in Error.cc.


void FontInfoScanner::scanFonts(XRef *xrefA, Dict *resDict, std::vector<FontInfo*> *fontsList) {
  GfxFontDict *gfxFontDict;
  GfxFont *font;

  // scan the fonts in this resource dictionary
  gfxFontDict = nullptr;
  const Object &fontObj = resDict->lookupNF("Font");
  if (fontObj.isRef()) {
    Object obj2 = fontObj.fetch(xrefA);
    if (obj2.isDict()) {
      Ref r = fontObj.getRef();
      gfxFontDict = new GfxFontDict(xrefA, &r, obj2.getDict());
    }
  } else if (fontObj.isDict()) {
    gfxFontDict = new GfxFontDict(xrefA, nullptr, fontObj.getDict());
  }
  if (gfxFontDict) {
    for (int i = 0; i < gfxFontDict->getNumFonts(); ++i) {
      if ((font = gfxFontDict->getFont(i))) {
        Ref fontRef = *font->getID();

        // add this font to the list if not already found
        if (fonts.find(fontRef.num) == fonts.end()) {
	  fontsList->push_back(new FontInfo(font, xrefA));
          fonts.insert(fontRef.num);
        }
      }
    }
    delete gfxFontDict;
  }

  // recursively scan any resource dictionaries in objects in this
  // resource dictionary
  const char *resTypes[] = { "XObject", "Pattern" };
  for (unsigned int resType = 0; resType < sizeof(resTypes) / sizeof(resTypes[0]); ++resType) {
    Object objDict = resDict->lookup(resTypes[resType]);
    if (objDict.isDict()) {
      for (int i = 0; i < objDict.dictGetLength(); ++i) {
        const Object &dictObjI = objDict.dictGetValNF(i);
        if (dictObjI.isRef()) {
          // check for an already-seen object
          const Ref r = dictObjI.getRef();
          if (visitedObjects.find(r.num) != visitedObjects.end()) {
            continue;
          }

          visitedObjects.insert(r.num);
        }

        Object obj2 = dictObjI.fetch(xrefA);
        if (obj2.isStream()) {
          Ref resourcesRef;
          const Object resObj = obj2.streamGetDict()->lookup("Resources", &resourcesRef);

          if (resourcesRef != Ref::INVALID()) {
            if (visitedObjects.find(resourcesRef.num) != visitedObjects.end()) {
              continue;
            }

            visitedObjects.insert(resourcesRef.num);
          }

          if (resObj.isDict() && resObj.getDict() != resDict) {
            scanFonts(xrefA, resObj.getDict(), fontsList);
          }
        }
      }
    }
  }
}


net: dsa: sja1105: Prevent leaking memory

In sja1105_static_config_upload, in two cases memory is leaked: when
static_config_buf_prepare_for_upload fails and when sja1105_inhibit_tx
fails. In both cases config_buf should be released.

Fixes: 8aa9ebccae87 ("net: dsa: Introduce driver for NXP SJA1105 5-port L2 switch")
Fixes: 1a4c69406cc1 ("net: dsa: sja1105: Prevent PHY jabbering during switch reset")
Signed-off-by: Navid Emamdoost <navid.emamdoost@gmail.com>
Signed-off-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

int sja1105_static_config_upload(struct sja1105_private *priv)
{
	unsigned long port_bitmap = GENMASK_ULL(SJA1105_NUM_PORTS - 1, 0);
	struct sja1105_static_config *config = &priv->static_config;
	const struct sja1105_regs *regs = priv->info->regs;
	struct device *dev = &priv->spidev->dev;
	struct sja1105_status status;
	int rc, retries = RETRIES;
	u8 *config_buf;
	int buf_len;

	buf_len = sja1105_static_config_get_length(config);
	config_buf = kcalloc(buf_len, sizeof(char), GFP_KERNEL);
	if (!config_buf)
		return -ENOMEM;

	rc = static_config_buf_prepare_for_upload(priv, config_buf, buf_len);
	if (rc < 0) {
		dev_err(dev, "Invalid config, cannot upload\n");
		return -EINVAL;
	}
	/* Prevent PHY jabbering during switch reset by inhibiting
	 * Tx on all ports and waiting for current packet to drain.
	 * Otherwise, the PHY will see an unterminated Ethernet packet.
	 */
	rc = sja1105_inhibit_tx(priv, port_bitmap, true);
	if (rc < 0) {
		dev_err(dev, "Failed to inhibit Tx on ports\n");
		return -ENXIO;
	}
	/* Wait for an eventual egress packet to finish transmission
	 * (reach IFG). It is guaranteed that a second one will not
	 * follow, and that switch cold reset is thus safe
	 */
	usleep_range(500, 1000);
	do {
		/* Put the SJA1105 in programming mode */
		rc = sja1105_cold_reset(priv);
		if (rc < 0) {
			dev_err(dev, "Failed to reset switch, retrying...\n");
			continue;
		}
		/* Wait for the switch to come out of reset */
		usleep_range(1000, 5000);
		/* Upload the static config to the device */
		rc = sja1105_spi_send_long_packed_buf(priv, SPI_WRITE,
						      regs->config,
						      config_buf, buf_len);
		if (rc < 0) {
			dev_err(dev, "Failed to upload config, retrying...\n");
			continue;
		}
		/* Check that SJA1105 responded well to the config upload */
		rc = sja1105_status_get(priv, &status);
		if (rc < 0)
			continue;

		if (status.ids == 1) {
			dev_err(dev, "Mismatch between hardware and static config "
				"device id. Wrote 0x%llx, wants 0x%llx\n",
				config->device_id, priv->info->device_id);
			continue;
		}
		if (status.crcchkl == 1) {
			dev_err(dev, "Switch reported invalid local CRC on "
				"the uploaded config, retrying...\n");
			continue;
		}
		if (status.crcchkg == 1) {
			dev_err(dev, "Switch reported invalid global CRC on "
				"the uploaded config, retrying...\n");
			continue;
		}
		if (status.configs == 0) {
			dev_err(dev, "Switch reported that configuration is "
				"invalid, retrying...\n");
			continue;
		}
		/* Success! */
		break;
	} while (--retries);

	if (!retries) {
		rc = -EIO;
		dev_err(dev, "Failed to upload config to device, giving up\n");
		goto out;
	} else if (retries != RETRIES) {
		dev_info(dev, "Succeeded after %d tried\n", RETRIES - retries);
	}

	rc = sja1105_ptp_reset(priv);
	if (rc < 0)
		dev_err(dev, "Failed to reset PTP clock: %d\n", rc);

	dev_info(dev, "Reset switch and programmed static config\n");

out:
	kfree(config_buf);
	return rc;
}

Two memory leaks in the sja1105_static_config_upload() function in drivers/net/dsa/sja1105/sja1105_spi.c in the Linux kernel before 5.3.5 allow attackers to cause a denial of service (memory consumption) by triggering static_config_buf_prepare_for_upload() or sja1105_inhibit_tx() failures, aka CID-68501df92d11.


int sja1105_static_config_upload(struct sja1105_private *priv)
{
	unsigned long port_bitmap = GENMASK_ULL(SJA1105_NUM_PORTS - 1, 0);
	struct sja1105_static_config *config = &priv->static_config;
	const struct sja1105_regs *regs = priv->info->regs;
	struct device *dev = &priv->spidev->dev;
	struct sja1105_status status;
	int rc, retries = RETRIES;
	u8 *config_buf;
	int buf_len;

	buf_len = sja1105_static_config_get_length(config);
	config_buf = kcalloc(buf_len, sizeof(char), GFP_KERNEL);
	if (!config_buf)
		return -ENOMEM;

	rc = static_config_buf_prepare_for_upload(priv, config_buf, buf_len);
	if (rc < 0) {
		dev_err(dev, "Invalid config, cannot upload\n");
		rc = -EINVAL;
		goto out;
	}
	/* Prevent PHY jabbering during switch reset by inhibiting
	 * Tx on all ports and waiting for current packet to drain.
	 * Otherwise, the PHY will see an unterminated Ethernet packet.
	 */
	rc = sja1105_inhibit_tx(priv, port_bitmap, true);
	if (rc < 0) {
		dev_err(dev, "Failed to inhibit Tx on ports\n");
		rc = -ENXIO;
		goto out;
	}
	/* Wait for an eventual egress packet to finish transmission
	 * (reach IFG). It is guaranteed that a second one will not
	 * follow, and that switch cold reset is thus safe
	 */
	usleep_range(500, 1000);
	do {
		/* Put the SJA1105 in programming mode */
		rc = sja1105_cold_reset(priv);
		if (rc < 0) {
			dev_err(dev, "Failed to reset switch, retrying...\n");
			continue;
		}
		/* Wait for the switch to come out of reset */
		usleep_range(1000, 5000);
		/* Upload the static config to the device */
		rc = sja1105_spi_send_long_packed_buf(priv, SPI_WRITE,
						      regs->config,
						      config_buf, buf_len);
		if (rc < 0) {
			dev_err(dev, "Failed to upload config, retrying...\n");
			continue;
		}
		/* Check that SJA1105 responded well to the config upload */
		rc = sja1105_status_get(priv, &status);
		if (rc < 0)
			continue;

		if (status.ids == 1) {
			dev_err(dev, "Mismatch between hardware and static config "
				"device id. Wrote 0x%llx, wants 0x%llx\n",
				config->device_id, priv->info->device_id);
			continue;
		}
		if (status.crcchkl == 1) {
			dev_err(dev, "Switch reported invalid local CRC on "
				"the uploaded config, retrying...\n");
			continue;
		}
		if (status.crcchkg == 1) {
			dev_err(dev, "Switch reported invalid global CRC on "
				"the uploaded config, retrying...\n");
			continue;
		}
		if (status.configs == 0) {
			dev_err(dev, "Switch reported that configuration is "
				"invalid, retrying...\n");
			continue;
		}
		/* Success! */
		break;
	} while (--retries);

	if (!retries) {
		rc = -EIO;
		dev_err(dev, "Failed to upload config to device, giving up\n");
		goto out;
	} else if (retries != RETRIES) {
		dev_info(dev, "Succeeded after %d tried\n", RETRIES - retries);
	}

	rc = sja1105_ptp_reset(priv);
	if (rc < 0)
		dev_err(dev, "Failed to reset PTP clock: %d\n", rc);

	dev_info(dev, "Reset switch and programmed static config\n");

out:
	kfree(config_buf);
	return rc;
}


Avoid negative integer overflow when `chunkLength == 0`.

This fixes #789.

    void PngImage::readMetadata()
    {
#ifdef DEBUG
        std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << std::endl;
#endif
        if (io_->open() != 0)
        {
            throw Error(kerDataSourceOpenFailed, io_->path(), strError());
        }
        IoCloser closer(*io_);
        if (!isPngType(*io_, true)) {
            throw Error(kerNotAnImage, "PNG");
        }
        clearMetadata();

        const long imgSize = (long) io_->size();
        DataBuf cheaderBuf(8);       // Chunk header: 4 bytes (data size) + 4 bytes (chunk type).

        while(!io_->eof())
        {
            std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_);
            readChunk(cheaderBuf, *io_); // Read chunk header.

            // Decode chunk data length.
            uint32_t chunkLength = Exiv2::getULong(cheaderBuf.pData_, Exiv2::bigEndian);
            long pos = io_->tell();
            if (pos == -1 ||
                chunkLength > uint32_t(0x7FFFFFFF) ||
                static_cast<long>(chunkLength) > imgSize - pos) {
                throw Exiv2::Error(kerFailedToReadImageData);
            }

            std::string chunkType(reinterpret_cast<char *>(cheaderBuf.pData_) + 4, 4);
#ifdef DEBUG
            std::cout << "Exiv2::PngImage::readMetadata: chunk type: " << chunkType
                      << " length: " << chunkLength << std::endl;
#endif

            /// \todo analyse remaining chunks of the standard
            // Perform a chunk triage for item that we need.
            if (chunkType == "IEND" || chunkType == "IHDR" || chunkType == "tEXt" || chunkType == "zTXt" ||
                chunkType == "iTXt" || chunkType == "iCCP") {
                DataBuf chunkData(chunkLength);
                readChunk(chunkData, *io_);  // Extract chunk data.

                if (chunkType == "IEND") {
                    return;  // Last chunk found: we stop parsing.
                } else if (chunkType == "IHDR" && chunkData.size_ >= 8) {
                    PngChunk::decodeIHDRChunk(chunkData, &pixelWidth_, &pixelHeight_);
                } else if (chunkType == "tEXt") {
                    PngChunk::decodeTXTChunk(this, chunkData, PngChunk::tEXt_Chunk);
                } else if (chunkType == "zTXt") {
                    PngChunk::decodeTXTChunk(this, chunkData, PngChunk::zTXt_Chunk);
                } else if (chunkType == "iTXt") {
                    PngChunk::decodeTXTChunk(this, chunkData, PngChunk::iTXt_Chunk);
                } else if (chunkType == "iCCP") {
                    // The ICC profile name can vary from 1-79 characters.
                    uint32_t iccOffset = 0;
                    while (iccOffset < 80 && iccOffset < chunkLength) {
                         if (chunkData.pData_[iccOffset++] ==  0x00) {
                            break;
                         }
                    }
                    profileName_ = std::string(reinterpret_cast<char *>(chunkData.pData_), iccOffset-1);
                    ++iccOffset; // +1 = 'compressed' flag
                    enforce(iccOffset <= chunkLength, Exiv2::kerCorruptedMetadata);

                    zlibToDataBuf(chunkData.pData_ + iccOffset, chunkLength - iccOffset, iccProfile_);
#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::readMetadata: profile name: " << profileName_ << std::endl;
                    std::cout << "Exiv2::PngImage::readMetadata: iccProfile.size_ (uncompressed) : "
                              << iccProfile_.size_ << std::endl;
#endif
                }

                // Set chunkLength to 0 in case we have read a supported chunk type. Otherwise, we need to seek the
                // file to the next chunk position.
                chunkLength = 0;
            }

            // Move to the next chunk: chunk data size + 4 CRC bytes.
#ifdef DEBUG
            std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << chunkLength + 4 << std::endl;
#endif
            io_->seek(chunkLength + 4 , BasicIo::cur);
            if (io_->error() || io_->eof()) {
                throw Error(kerFailedToReadImageData);
            }
        }
    } // PngImage::readMetadata

An integer overflow in Exiv2 through 0.27.1 allows an attacker to cause a denial of service (SIGSEGV) via a crafted PNG image file, because PngImage::readMetadata mishandles a zero value for iccOffset.


    void PngImage::readMetadata()
    {
#ifdef DEBUG
        std::cerr << "Exiv2::PngImage::readMetadata: Reading PNG file " << io_->path() << std::endl;
#endif
        if (io_->open() != 0)
        {
            throw Error(kerDataSourceOpenFailed, io_->path(), strError());
        }
        IoCloser closer(*io_);
        if (!isPngType(*io_, true)) {
            throw Error(kerNotAnImage, "PNG");
        }
        clearMetadata();

        const long imgSize = (long) io_->size();
        DataBuf cheaderBuf(8);       // Chunk header: 4 bytes (data size) + 4 bytes (chunk type).

        while(!io_->eof())
        {
            std::memset(cheaderBuf.pData_, 0x0, cheaderBuf.size_);
            readChunk(cheaderBuf, *io_); // Read chunk header.

            // Decode chunk data length.
            uint32_t chunkLength = Exiv2::getULong(cheaderBuf.pData_, Exiv2::bigEndian);
            long pos = io_->tell();
            if (pos == -1 ||
                chunkLength > uint32_t(0x7FFFFFFF) ||
                static_cast<long>(chunkLength) > imgSize - pos) {
                throw Exiv2::Error(kerFailedToReadImageData);
            }

            std::string chunkType(reinterpret_cast<char *>(cheaderBuf.pData_) + 4, 4);
#ifdef DEBUG
            std::cout << "Exiv2::PngImage::readMetadata: chunk type: " << chunkType
                      << " length: " << chunkLength << std::endl;
#endif

            /// \todo analyse remaining chunks of the standard
            // Perform a chunk triage for item that we need.
            if (chunkType == "IEND" || chunkType == "IHDR" || chunkType == "tEXt" || chunkType == "zTXt" ||
                chunkType == "iTXt" || chunkType == "iCCP") {
                DataBuf chunkData(chunkLength);
                readChunk(chunkData, *io_);  // Extract chunk data.

                if (chunkType == "IEND") {
                    return;  // Last chunk found: we stop parsing.
                } else if (chunkType == "IHDR" && chunkData.size_ >= 8) {
                    PngChunk::decodeIHDRChunk(chunkData, &pixelWidth_, &pixelHeight_);
                } else if (chunkType == "tEXt") {
                    PngChunk::decodeTXTChunk(this, chunkData, PngChunk::tEXt_Chunk);
                } else if (chunkType == "zTXt") {
                    PngChunk::decodeTXTChunk(this, chunkData, PngChunk::zTXt_Chunk);
                } else if (chunkType == "iTXt") {
                    PngChunk::decodeTXTChunk(this, chunkData, PngChunk::iTXt_Chunk);
                } else if (chunkType == "iCCP") {
                    // The ICC profile name can vary from 1-79 characters.
                    uint32_t iccOffset = 0;
                    do {
                      enforce(iccOffset < 80 && iccOffset < chunkLength,
                              Exiv2::kerCorruptedMetadata);
                    } while(chunkData.pData_[iccOffset++] != 0x00);

                    profileName_ = std::string(reinterpret_cast<char *>(chunkData.pData_), iccOffset-1);
                    ++iccOffset; // +1 = 'compressed' flag
                    enforce(iccOffset <= chunkLength, Exiv2::kerCorruptedMetadata);

                    zlibToDataBuf(chunkData.pData_ + iccOffset, chunkLength - iccOffset, iccProfile_);
#ifdef DEBUG
                    std::cout << "Exiv2::PngImage::readMetadata: profile name: " << profileName_ << std::endl;
                    std::cout << "Exiv2::PngImage::readMetadata: iccProfile.size_ (uncompressed) : "
                              << iccProfile_.size_ << std::endl;
#endif
                }

                // Set chunkLength to 0 in case we have read a supported chunk type. Otherwise, we need to seek the
                // file to the next chunk position.
                chunkLength = 0;
            }

            // Move to the next chunk: chunk data size + 4 CRC bytes.
#ifdef DEBUG
            std::cout << "Exiv2::PngImage::readMetadata: Seek to offset: " << chunkLength + 4 << std::endl;
#endif
            io_->seek(chunkLength + 4 , BasicIo::cur);
            if (io_->error() || io_->eof()) {
                throw Error(kerFailedToReadImageData);
            }
        }
    } // PngImage::readMetadata


static void huffman_decode_row(x3f_info_t *I, x3f_directory_entry_t *DE,
                               int bits, int row, int offset, int *minimum)
{
  x3f_directory_entry_header_t *DEH = &DE->header;
  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
  x3f_huffman_t *HUF = ID->huffman;

  int16_t c[3] = {(int16_t)offset, (int16_t)offset, (int16_t)offset};
  int col;
  bit_state_t BS;

  set_bit_state(&BS, (uint8_t *)ID->data + HUF->row_offsets.element[row]);

  for (col = 0; col < ID->columns; col++)
  {
    int color;

    for (color = 0; color < 3; color++)
    {
      uint16_t c_fix;

      c[color] += get_huffman_diff(&BS, &HUF->tree);
      if (c[color] < 0)
      {
        c_fix = 0;
        if (c[color] < *minimum)
          *minimum = c[color];
      }
      else
      {
        c_fix = c[color];
      }

      switch (ID->type_format)
      {
      case X3F_IMAGE_RAW_HUFFMAN_X530:
      case X3F_IMAGE_RAW_HUFFMAN_10BIT:
        HUF->x3rgb16.data[3 * (row * ID->columns + col) + color] =
            (uint16_t)c_fix;
        break;
      case X3F_IMAGE_THUMB_HUFFMAN:
        HUF->rgb8.data[3 * (row * ID->columns + col) + color] = (uint8_t)c_fix;
        break;
      default:
        /* TODO: Shouldn't this be treated as a fatal error? */
        throw LIBRAW_EXCEPTION_IO_CORRUPT;
      }
    }
  }
}

In LibRaw, an out-of-bounds read vulnerability exists within the get_huffman_diff() function (libraw\src\x3f\x3f_utils_patched.cpp) when reading data from an image file.


static void huffman_decode_row(x3f_info_t *I, x3f_directory_entry_t *DE,
                               int bits, int row, int offset, int *minimum)
{
  x3f_directory_entry_header_t *DEH = &DE->header;
  x3f_image_data_t *ID = &DEH->data_subsection.image_data;
  x3f_huffman_t *HUF = ID->huffman;

  int16_t c[3] = {(int16_t)offset, (int16_t)offset, (int16_t)offset};
  int col;
  bit_state_t BS;

  if (HUF->row_offsets.element[row] > ID->data_size - 1)
	  throw LIBRAW_EXCEPTION_IO_CORRUPT;
  set_bit_state(&BS, (uint8_t *)ID->data + HUF->row_offsets.element[row]);

  for (col = 0; col < ID->columns; col++)
  {
    int color;

    for (color = 0; color < 3; color++)
    {
      uint16_t c_fix;

      c[color] += get_huffman_diff(&BS, &HUF->tree);
      if (c[color] < 0)
      {
        c_fix = 0;
        if (c[color] < *minimum)
          *minimum = c[color];
      }
      else
      {
        c_fix = c[color];
      }

      switch (ID->type_format)
      {
      case X3F_IMAGE_RAW_HUFFMAN_X530:
      case X3F_IMAGE_RAW_HUFFMAN_10BIT:
        HUF->x3rgb16.data[3 * (row * ID->columns + col) + color] =
            (uint16_t)c_fix;
        break;
      case X3F_IMAGE_THUMB_HUFFMAN:
        HUF->rgb8.data[3 * (row * ID->columns + col) + color] = (uint8_t)c_fix;
        break;
      default:
        /* TODO: Shouldn't this be treated as a fatal error? */
        throw LIBRAW_EXCEPTION_IO_CORRUPT;
      }
    }
  }
}


Prevent players accessing inventories of other players (#10341)

void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
{
	session_t peer_id = pkt->getPeerId();
	RemotePlayer *player = m_env->getPlayer(peer_id);

	if (player == NULL) {
		errorstream <<
			"Server::ProcessData(): Canceling: No player for peer_id=" <<
			peer_id << " disconnecting peer!" << std::endl;
		DisconnectPeer(peer_id);
		return;
	}

	PlayerSAO *playersao = player->getPlayerSAO();
	if (playersao == NULL) {
		errorstream <<
			"Server::ProcessData(): Canceling: No player object for peer_id=" <<
			peer_id << " disconnecting peer!" << std::endl;
		DisconnectPeer(peer_id);
		return;
	}

	// Strip command and create a stream
	std::string datastring(pkt->getString(0), pkt->getSize());
	verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
		<< std::endl;
	std::istringstream is(datastring, std::ios_base::binary);
	// Create an action
	InventoryAction *a = InventoryAction::deSerialize(is);
	if (!a) {
		infostream << "TOSERVER_INVENTORY_ACTION: "
				<< "InventoryAction::deSerialize() returned NULL"
				<< std::endl;
		return;
	}

	// If something goes wrong, this player is to blame
	RollbackScopeActor rollback_scope(m_rollback,
			std::string("player:")+player->getName());

	/*
		Note: Always set inventory not sent, to repair cases
		where the client made a bad prediction.
	*/

	/*
		Handle restrictions and special cases of the move action
	*/
	if (a->getType() == IAction::Move) {
		IMoveAction *ma = (IMoveAction*)a;

		ma->from_inv.applyCurrentPlayer(player->getName());
		ma->to_inv.applyCurrentPlayer(player->getName());

		m_inventory_mgr->setInventoryModified(ma->from_inv);
		if (ma->from_inv != ma->to_inv)
			m_inventory_mgr->setInventoryModified(ma->to_inv);

		bool from_inv_is_current_player =
			(ma->from_inv.type == InventoryLocation::PLAYER) &&
			(ma->from_inv.name == player->getName());

		bool to_inv_is_current_player =
			(ma->to_inv.type == InventoryLocation::PLAYER) &&
			(ma->to_inv.name == player->getName());

		InventoryLocation *remote = from_inv_is_current_player ?
			&ma->to_inv : &ma->from_inv;

		// Check for out-of-range interaction
		if (remote->type == InventoryLocation::NODEMETA) {
			v3f node_pos   = intToFloat(remote->p, BS);
			v3f player_pos = player->getPlayerSAO()->getEyePosition();
			f32 d = player_pos.getDistanceFrom(node_pos);
			if (!checkInteractDistance(player, d, "inventory"))
				return;
		}

		/*
			Disable moving items out of craftpreview
		*/
		if (ma->from_list == "craftpreview") {
			infostream << "Ignoring IMoveAction from "
					<< (ma->from_inv.dump()) << ":" << ma->from_list
					<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
					<< " because src is " << ma->from_list << std::endl;
			delete a;
			return;
		}

		/*
			Disable moving items into craftresult and craftpreview
		*/
		if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
			infostream << "Ignoring IMoveAction from "
					<< (ma->from_inv.dump()) << ":" << ma->from_list
					<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
					<< " because dst is " << ma->to_list << std::endl;
			delete a;
			return;
		}

		// Disallow moving items in elsewhere than player's inventory
		// if not allowed to interact
		if (!checkPriv(player->getName(), "interact") &&
				(!from_inv_is_current_player ||
				!to_inv_is_current_player)) {
			infostream << "Cannot move outside of player's inventory: "
					<< "No interact privilege" << std::endl;
			delete a;
			return;
		}
	}
	/*
		Handle restrictions and special cases of the drop action
	*/
	else if (a->getType() == IAction::Drop) {
		IDropAction *da = (IDropAction*)a;

		da->from_inv.applyCurrentPlayer(player->getName());

		m_inventory_mgr->setInventoryModified(da->from_inv);

		/*
			Disable dropping items out of craftpreview
		*/
		if (da->from_list == "craftpreview") {
			infostream << "Ignoring IDropAction from "
					<< (da->from_inv.dump()) << ":" << da->from_list
					<< " because src is " << da->from_list << std::endl;
			delete a;
			return;
		}

		// Disallow dropping items if not allowed to interact
		if (!checkPriv(player->getName(), "interact")) {
			delete a;
			return;
		}

		// Disallow dropping items if dead
		if (playersao->isDead()) {
			infostream << "Ignoring IDropAction from "
					<< (da->from_inv.dump()) << ":" << da->from_list
					<< " because player is dead." << std::endl;
			delete a;
			return;
		}
	}
	/*
		Handle restrictions and special cases of the craft action
	*/
	else if (a->getType() == IAction::Craft) {
		ICraftAction *ca = (ICraftAction*)a;

		ca->craft_inv.applyCurrentPlayer(player->getName());

		m_inventory_mgr->setInventoryModified(ca->craft_inv);

		//bool craft_inv_is_current_player =
		//	(ca->craft_inv.type == InventoryLocation::PLAYER) &&
		//	(ca->craft_inv.name == player->getName());

		// Disallow crafting if not allowed to interact
		if (!checkPriv(player->getName(), "interact")) {
			infostream << "Cannot craft: "
					<< "No interact privilege" << std::endl;
			delete a;
			return;
		}
	}

	// Do the action
	a->apply(m_inventory_mgr.get(), playersao, this);
	// Eat the action
	delete a;
}

In Minetest before 5.4.0, players can add or subtract items from a different player's inventory.


void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
{
	session_t peer_id = pkt->getPeerId();
	RemotePlayer *player = m_env->getPlayer(peer_id);

	if (player == NULL) {
		errorstream <<
			"Server::ProcessData(): Canceling: No player for peer_id=" <<
			peer_id << " disconnecting peer!" << std::endl;
		DisconnectPeer(peer_id);
		return;
	}

	PlayerSAO *playersao = player->getPlayerSAO();
	if (playersao == NULL) {
		errorstream <<
			"Server::ProcessData(): Canceling: No player object for peer_id=" <<
			peer_id << " disconnecting peer!" << std::endl;
		DisconnectPeer(peer_id);
		return;
	}

	// Strip command and create a stream
	std::string datastring(pkt->getString(0), pkt->getSize());
	verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
		<< std::endl;
	std::istringstream is(datastring, std::ios_base::binary);
	// Create an action
	InventoryAction *a = InventoryAction::deSerialize(is);
	if (!a) {
		infostream << "TOSERVER_INVENTORY_ACTION: "
				<< "InventoryAction::deSerialize() returned NULL"
				<< std::endl;
		return;
	}

	// If something goes wrong, this player is to blame
	RollbackScopeActor rollback_scope(m_rollback,
			std::string("player:")+player->getName());

	/*
		Note: Always set inventory not sent, to repair cases
		where the client made a bad prediction.
	*/

	/*
		Handle restrictions and special cases of the move action
	*/
	if (a->getType() == IAction::Move) {
		IMoveAction *ma = (IMoveAction*)a;

		ma->from_inv.applyCurrentPlayer(player->getName());
		ma->to_inv.applyCurrentPlayer(player->getName());

		m_inventory_mgr->setInventoryModified(ma->from_inv);
		if (ma->from_inv != ma->to_inv)
			m_inventory_mgr->setInventoryModified(ma->to_inv);

		bool from_inv_is_current_player = false;
		if (ma->from_inv.type == InventoryLocation::PLAYER) {
			if (ma->from_inv.name != player->getName())
				return;
			from_inv_is_current_player = true;
		}

		bool to_inv_is_current_player = false;
		if (ma->to_inv.type == InventoryLocation::PLAYER) {
			if (ma->to_inv.name != player->getName())
				return;
			to_inv_is_current_player = true;
		}

		InventoryLocation *remote = from_inv_is_current_player ?
			&ma->to_inv : &ma->from_inv;

		// Check for out-of-range interaction
		if (remote->type == InventoryLocation::NODEMETA) {
			v3f node_pos   = intToFloat(remote->p, BS);
			v3f player_pos = player->getPlayerSAO()->getEyePosition();
			f32 d = player_pos.getDistanceFrom(node_pos);
			if (!checkInteractDistance(player, d, "inventory"))
				return;
		}

		/*
			Disable moving items out of craftpreview
		*/
		if (ma->from_list == "craftpreview") {
			infostream << "Ignoring IMoveAction from "
					<< (ma->from_inv.dump()) << ":" << ma->from_list
					<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
					<< " because src is " << ma->from_list << std::endl;
			delete a;
			return;
		}

		/*
			Disable moving items into craftresult and craftpreview
		*/
		if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
			infostream << "Ignoring IMoveAction from "
					<< (ma->from_inv.dump()) << ":" << ma->from_list
					<< " to " << (ma->to_inv.dump()) << ":" << ma->to_list
					<< " because dst is " << ma->to_list << std::endl;
			delete a;
			return;
		}

		// Disallow moving items in elsewhere than player's inventory
		// if not allowed to interact
		if (!checkPriv(player->getName(), "interact") &&
				(!from_inv_is_current_player ||
				!to_inv_is_current_player)) {
			infostream << "Cannot move outside of player's inventory: "
					<< "No interact privilege" << std::endl;
			delete a;
			return;
		}
	}
	/*
		Handle restrictions and special cases of the drop action
	*/
	else if (a->getType() == IAction::Drop) {
		IDropAction *da = (IDropAction*)a;

		da->from_inv.applyCurrentPlayer(player->getName());

		m_inventory_mgr->setInventoryModified(da->from_inv);

		/*
			Disable dropping items out of craftpreview
		*/
		if (da->from_list == "craftpreview") {
			infostream << "Ignoring IDropAction from "
					<< (da->from_inv.dump()) << ":" << da->from_list
					<< " because src is " << da->from_list << std::endl;
			delete a;
			return;
		}

		// Disallow dropping items if not allowed to interact
		if (!checkPriv(player->getName(), "interact")) {
			delete a;
			return;
		}

		// Disallow dropping items if dead
		if (playersao->isDead()) {
			infostream << "Ignoring IDropAction from "
					<< (da->from_inv.dump()) << ":" << da->from_list
					<< " because player is dead." << std::endl;
			delete a;
			return;
		}
	}
	/*
		Handle restrictions and special cases of the craft action
	*/
	else if (a->getType() == IAction::Craft) {
		ICraftAction *ca = (ICraftAction*)a;

		ca->craft_inv.applyCurrentPlayer(player->getName());

		m_inventory_mgr->setInventoryModified(ca->craft_inv);

		//bool craft_inv_is_current_player =
		//	(ca->craft_inv.type == InventoryLocation::PLAYER) &&
		//	(ca->craft_inv.name == player->getName());

		// Disallow crafting if not allowed to interact
		if (!checkPriv(player->getName(), "interact")) {
			infostream << "Cannot craft: "
					<< "No interact privilege" << std::endl;
			delete a;
			return;
		}
	}

	// Do the action
	a->apply(m_inventory_mgr.get(), playersao, this);
	// Eat the action
	delete a;
}


- Fix Out of Bound Write Compressed Names in rdata_copy(),
  reported by X41 D-Sec.

rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
	struct rr_parse* rr, time_t* rr_ttl, uint16_t type,
	sldns_pkt_section section)
{
	uint16_t pkt_len;
	const sldns_rr_descriptor* desc;

	*rr_ttl = sldns_read_uint32(rr->ttl_data);
	/* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */
	if(*rr_ttl & 0x80000000U)
		*rr_ttl = 0;
	if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) {
		/* negative response. see if TTL of SOA record larger than the
		 * minimum-ttl in the rdata of the SOA record */
		if(*rr_ttl > soa_find_minttl(rr))
			*rr_ttl = soa_find_minttl(rr);
		if(*rr_ttl > MAX_NEG_TTL)
			*rr_ttl = MAX_NEG_TTL;
	}
	if(*rr_ttl < MIN_TTL)
		*rr_ttl = MIN_TTL;
	if(*rr_ttl > MAX_TTL)
		*rr_ttl = MAX_TTL;
	if(*rr_ttl < data->ttl)
		data->ttl = *rr_ttl;

	if(rr->outside_packet) {
		/* uncompressed already, only needs copy */
		memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size);
		return 1;
	}

	sldns_buffer_set_position(pkt, (size_t)
		(rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t)));
	/* insert decompressed size into rdata len stored in memory */
	/* -2 because rdatalen bytes are not included. */
	pkt_len = htons(rr->size - 2);
	memmove(to, &pkt_len, sizeof(uint16_t));
	to += 2;
	/* read packet rdata len */
	pkt_len = sldns_buffer_read_u16(pkt);
	if(sldns_buffer_remaining(pkt) < pkt_len)
		return 0;
	desc = sldns_rr_descript(type);
	if(pkt_len > 0 && desc && desc->_dname_count > 0) {
		int count = (int)desc->_dname_count;
		int rdf = 0;
		size_t len;
		size_t oldpos;
		/* decompress dnames. */
		while(pkt_len > 0 && count) {
			switch(desc->_wireformat[rdf]) {
			case LDNS_RDF_TYPE_DNAME:
				oldpos = sldns_buffer_position(pkt);
				dname_pkt_copy(pkt, to,
					sldns_buffer_current(pkt));
				to += pkt_dname_len(pkt);
				pkt_len -= sldns_buffer_position(pkt)-oldpos;
				count--;
				len = 0;
				break;
			case LDNS_RDF_TYPE_STR:
				len = sldns_buffer_current(pkt)[0] + 1;
				break;
			default:
				len = get_rdf_size(desc->_wireformat[rdf]);
				break;
			}
			if(len) {
				memmove(to, sldns_buffer_current(pkt), len);
				to += len;
				sldns_buffer_skip(pkt, (ssize_t)len);
				log_assert(len <= pkt_len);
				pkt_len -= len;
			}
			rdf++;
		}
	}
	/* copy remaining rdata */
	if(pkt_len >  0)
		memmove(to, sldns_buffer_current(pkt), pkt_len);

	return 1;
}

Unbound before 1.9.5 allows an out-of-bounds write via a compressed name in rdata_copy. NOTE: The vendor disputes that this is a vulnerability. Although the code may be vulnerable, a running Unbound installation cannot be remotely or locally exploited


rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
	struct rr_parse* rr, time_t* rr_ttl, uint16_t type,
	sldns_pkt_section section)
{
	uint16_t pkt_len;
	const sldns_rr_descriptor* desc;

	*rr_ttl = sldns_read_uint32(rr->ttl_data);
	/* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */
	if(*rr_ttl & 0x80000000U)
		*rr_ttl = 0;
	if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) {
		/* negative response. see if TTL of SOA record larger than the
		 * minimum-ttl in the rdata of the SOA record */
		if(*rr_ttl > soa_find_minttl(rr))
			*rr_ttl = soa_find_minttl(rr);
		if(*rr_ttl > MAX_NEG_TTL)
			*rr_ttl = MAX_NEG_TTL;
	}
	if(*rr_ttl < MIN_TTL)
		*rr_ttl = MIN_TTL;
	if(*rr_ttl > MAX_TTL)
		*rr_ttl = MAX_TTL;
	if(*rr_ttl < data->ttl)
		data->ttl = *rr_ttl;

	if(rr->outside_packet) {
		/* uncompressed already, only needs copy */
		memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size);
		return 1;
	}

	sldns_buffer_set_position(pkt, (size_t)
		(rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t)));
	/* insert decompressed size into rdata len stored in memory */
	/* -2 because rdatalen bytes are not included. */
	pkt_len = htons(rr->size - 2);
	memmove(to, &pkt_len, sizeof(uint16_t));
	to += 2;
	/* read packet rdata len */
	pkt_len = sldns_buffer_read_u16(pkt);
	if(sldns_buffer_remaining(pkt) < pkt_len)
		return 0;
	desc = sldns_rr_descript(type);
	if(pkt_len > 0 && desc && desc->_dname_count > 0) {
		int count = (int)desc->_dname_count;
		int rdf = 0;
		size_t len;
		size_t oldpos;
		/* decompress dnames. */
		while(pkt_len > 0 && count) {
			switch(desc->_wireformat[rdf]) {
			case LDNS_RDF_TYPE_DNAME:
				oldpos = sldns_buffer_position(pkt);
				dname_pkt_copy(pkt, to,
					sldns_buffer_current(pkt));
				to += pkt_dname_len(pkt);
				pkt_len -= sldns_buffer_position(pkt)-oldpos;
				count--;
				len = 0;
				break;
			case LDNS_RDF_TYPE_STR:
				len = sldns_buffer_current(pkt)[0] + 1;
				break;
			default:
				len = get_rdf_size(desc->_wireformat[rdf]);
				break;
			}
			if(len) {
				log_assert(len <= pkt_len);
				memmove(to, sldns_buffer_current(pkt), len);
				to += len;
				sldns_buffer_skip(pkt, (ssize_t)len);
				pkt_len -= len;
			}
			rdf++;
		}
	}
	/* copy remaining rdata */
	if(pkt_len >  0)
		memmove(to, sldns_buffer_current(pkt), pkt_len);

	return 1;
}


Fixed leaking of the image when writing an MSL image (#1360).

static MagickBooleanType WriteMSLImage(const ImageInfo *image_info,Image *image,
  ExceptionInfo *exception)
{
  Image
    *msl_image;

  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  msl_image=CloneImage(image,0,0,MagickTrue,exception);
  return(ProcessMSLScript(image_info,&msl_image,exception));
}

There is a memory leak in the function WriteMSLImage of coders/msl.c in ImageMagick 7.0.8-13 Q16, and the function ProcessMSLScript of coders/msl.c in GraphicsMagick before 1.3.31.


static MagickBooleanType WriteMSLImage(const ImageInfo *image_info,Image *image,
  ExceptionInfo *exception)
{
  Image
    *msl_image;

  MagickBooleanType
    status;

  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  msl_image=CloneImage(image,0,0,MagickTrue,exception);
  status=ProcessMSLScript(image_info,&msl_image,exception);
  msl_image=DestroyImageList(msl_image);
  return(status);
}


Prevent possible IMAP MITM via PREAUTH response.

This is similar to CVE-2014-2567 and CVE-2020-12398.  STARTTLS is not
allowed in the Authenticated state, so previously Mutt would
implicitly mark the connection as authenticated and skip any
encryption checking/enabling.

No credentials are exposed, but it does allow messages to be sent to
an attacker, via postpone or fcc'ing for instance.

Reuse the $ssl_starttls quadoption "in reverse" to prompt to abort the
connection if it is unencrypted.

Thanks very much to Damian Poddebniak and Fabian Ising from the
Münster University of Applied Sciences for reporting this issue, and
their help in testing the fix.

int imap_open_connection (IMAP_DATA* idata)
{
  if (mutt_socket_open (idata->conn) < 0)
    return -1;

  idata->state = IMAP_CONNECTED;

  if (imap_cmd_step (idata) != IMAP_CMD_OK)
  {
    imap_close_connection (idata);
    return -1;
  }

  if (ascii_strncasecmp ("* OK", idata->buf, 4) == 0)
  {
    if (ascii_strncasecmp ("* OK [CAPABILITY", idata->buf, 16)
        && imap_check_capabilities (idata))
      goto bail;
#if defined(USE_SSL)
    /* Attempt STARTTLS if available and desired. */
    if (!idata->conn->ssf && (option(OPTSSLFORCETLS) ||
                              mutt_bit_isset (idata->capabilities, STARTTLS)))
    {
      int rc;

      if (option(OPTSSLFORCETLS))
        rc = MUTT_YES;
      else if ((rc = query_quadoption (OPT_SSLSTARTTLS,
                                       _("Secure connection with TLS?"))) == -1)
	goto err_close_conn;
      if (rc == MUTT_YES)
      {
	if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
	  goto bail;
	if (rc != -2)
	{
	  if (mutt_ssl_starttls (idata->conn))
	  {
	    mutt_error (_("Could not negotiate TLS connection"));
	    mutt_sleep (1);
	    goto err_close_conn;
	  }
	  else
	  {
	    /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
	    if (imap_exec (idata, "CAPABILITY", 0))
	      goto bail;
	  }
	}
      }
    }

    if (option(OPTSSLFORCETLS) && ! idata->conn->ssf)
    {
      mutt_error _("Encrypted connection unavailable");
      mutt_sleep (1);
      goto err_close_conn;
    }
#endif
  }
  else if (ascii_strncasecmp ("* PREAUTH", idata->buf, 9) == 0)
  {
    idata->state = IMAP_AUTHENTICATED;
    if (imap_check_capabilities (idata) != 0)
      goto bail;
    FREE (&idata->capstr);
  }
  else
  {
    imap_error ("imap_open_connection()", idata->buf);
    goto bail;
  }

  return 0;

#if defined(USE_SSL)
err_close_conn:
  imap_close_connection (idata);
#endif
bail:
  FREE (&idata->capstr);
  return -1;
}

Mutt before 1.14.3 allows an IMAP fcc/postpone man-in-the-middle attack via a PREAUTH response.


int imap_open_connection (IMAP_DATA* idata)
{
  if (mutt_socket_open (idata->conn) < 0)
    return -1;

  idata->state = IMAP_CONNECTED;

  if (imap_cmd_step (idata) != IMAP_CMD_OK)
  {
    imap_close_connection (idata);
    return -1;
  }

  if (ascii_strncasecmp ("* OK", idata->buf, 4) == 0)
  {
    if (ascii_strncasecmp ("* OK [CAPABILITY", idata->buf, 16)
        && imap_check_capabilities (idata))
      goto bail;
#if defined(USE_SSL)
    /* Attempt STARTTLS if available and desired. */
    if (!idata->conn->ssf && (option(OPTSSLFORCETLS) ||
                              mutt_bit_isset (idata->capabilities, STARTTLS)))
    {
      int rc;

      if (option(OPTSSLFORCETLS))
        rc = MUTT_YES;
      else if ((rc = query_quadoption (OPT_SSLSTARTTLS,
                                       _("Secure connection with TLS?"))) == -1)
	goto err_close_conn;
      if (rc == MUTT_YES)
      {
	if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
	  goto bail;
	if (rc != -2)
	{
	  if (mutt_ssl_starttls (idata->conn))
	  {
	    mutt_error (_("Could not negotiate TLS connection"));
	    mutt_sleep (1);
	    goto err_close_conn;
	  }
	  else
	  {
	    /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
	    if (imap_exec (idata, "CAPABILITY", 0))
	      goto bail;
	  }
	}
      }
    }

    if (option(OPTSSLFORCETLS) && ! idata->conn->ssf)
    {
      mutt_error _("Encrypted connection unavailable");
      mutt_sleep (1);
      goto err_close_conn;
    }
#endif
  }
  else if (ascii_strncasecmp ("* PREAUTH", idata->buf, 9) == 0)
  {
#if defined(USE_SSL)
    /* An unencrypted PREAUTH response is most likely a MITM attack.
     * Require a confirmation. */
    if (!idata->conn->ssf)
    {
      if (option(OPTSSLFORCETLS) ||
          (query_quadoption (OPT_SSLSTARTTLS,
                             _("Abort unencrypted PREAUTH connection?")) != MUTT_NO))
      {
        mutt_error _("Encrypted connection unavailable");
        mutt_sleep (1);
        goto err_close_conn;
      }
    }
#endif

    idata->state = IMAP_AUTHENTICATED;
    if (imap_check_capabilities (idata) != 0)
      goto bail;
    FREE (&idata->capstr);
  }
  else
  {
    imap_error ("imap_open_connection()", idata->buf);
    goto bail;
  }

  return 0;

#if defined(USE_SSL)
err_close_conn:
  imap_close_connection (idata);
#endif
bail:
  FREE (&idata->capstr);
  return -1;
}


Fix bug #68113 (Heap corruption in exif_thumbnail())

static void* exif_ifd_make_value(image_info_data *info_data, int motorola_intel TSRMLS_DC) {
	size_t  byte_count;
	char    *value_ptr, *data_ptr;
	size_t  i;

	image_info_value  *info_value;

	byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
	value_ptr = safe_emalloc(max(byte_count, 4), 1, 0);
	memset(value_ptr, 0, 4);
	if (!info_data->length) {
		return value_ptr;
	}
	if (info_data->format == TAG_FMT_UNDEFINED || info_data->format == TAG_FMT_STRING
	  || (byte_count>1 && (info_data->format == TAG_FMT_BYTE || info_data->format == TAG_FMT_SBYTE))
	) {
		memmove(value_ptr, info_data->value.s, byte_count);
		return value_ptr;
	} else if (info_data->format == TAG_FMT_BYTE) {
		*value_ptr = info_data->value.u;
		return value_ptr;
	} else if (info_data->format == TAG_FMT_SBYTE) {
		*value_ptr = info_data->value.i;
		return value_ptr;
	} else {
		data_ptr = value_ptr;
		for(i=0; i<info_data->length; i++) {
			if (info_data->length==1) {
				info_value = &info_data->value;
			} else {
				info_value = &info_data->value.list[i];
			}
			switch(info_data->format) {
				case TAG_FMT_USHORT:
					php_ifd_set16u(data_ptr, info_value->u, motorola_intel);
					data_ptr += 2;
					break;
				case TAG_FMT_ULONG:
					php_ifd_set32u(data_ptr, info_value->u, motorola_intel);
					data_ptr += 4;
					break;
				case TAG_FMT_SSHORT:
					php_ifd_set16u(data_ptr, info_value->i, motorola_intel);
					data_ptr += 2;
					break;
				case TAG_FMT_SLONG:
					php_ifd_set32u(data_ptr, info_value->i, motorola_intel);
					data_ptr += 4;
					break;
				case TAG_FMT_URATIONAL:
					php_ifd_set32u(data_ptr,   info_value->sr.num, motorola_intel);
					php_ifd_set32u(data_ptr+4, info_value->sr.den, motorola_intel);
					data_ptr += 8;
					break;
				case TAG_FMT_SRATIONAL:
					php_ifd_set32u(data_ptr,   info_value->ur.num, motorola_intel);
					php_ifd_set32u(data_ptr+4, info_value->ur.den, motorola_intel);
					data_ptr += 8;
					break;
				case TAG_FMT_SINGLE:
					memmove(data_ptr, &info_data->value.f, byte_count);
					data_ptr += 4;
					break;
				case TAG_FMT_DOUBLE:
					memmove(data_ptr, &info_data->value.d, byte_count);
					data_ptr += 8;
					break;
			}
		}
	}
	return value_ptr;
}

The exif_ifd_make_value function in exif.c in the EXIF extension in PHP before 5.4.34, 5.5.x before 5.5.18, and 5.6.x before 5.6.2 operates on floating-point arrays incorrectly, which allows remote attackers to cause a denial of service (heap memory corruption and application crash) or possibly execute arbitrary code via a crafted JPEG image with TIFF thumbnail data that is improperly handled by the exif_thumbnail function.


static void* exif_ifd_make_value(image_info_data *info_data, int motorola_intel TSRMLS_DC) {
	size_t  byte_count;
	char    *value_ptr, *data_ptr;
	size_t  i;

	image_info_value  *info_value;

	byte_count = php_tiff_bytes_per_format[info_data->format] * info_data->length;
	value_ptr = safe_emalloc(max(byte_count, 4), 1, 0);
	memset(value_ptr, 0, 4);
	if (!info_data->length) {
		return value_ptr;
	}
	if (info_data->format == TAG_FMT_UNDEFINED || info_data->format == TAG_FMT_STRING
	  || (byte_count>1 && (info_data->format == TAG_FMT_BYTE || info_data->format == TAG_FMT_SBYTE))
	) {
		memmove(value_ptr, info_data->value.s, byte_count);
		return value_ptr;
	} else if (info_data->format == TAG_FMT_BYTE) {
		*value_ptr = info_data->value.u;
		return value_ptr;
	} else if (info_data->format == TAG_FMT_SBYTE) {
		*value_ptr = info_data->value.i;
		return value_ptr;
	} else {
		data_ptr = value_ptr;
		for(i=0; i<info_data->length; i++) {
			if (info_data->length==1) {
				info_value = &info_data->value;
			} else {
				info_value = &info_data->value.list[i];
			}
			switch(info_data->format) {
				case TAG_FMT_USHORT:
					php_ifd_set16u(data_ptr, info_value->u, motorola_intel);
					data_ptr += 2;
					break;
				case TAG_FMT_ULONG:
					php_ifd_set32u(data_ptr, info_value->u, motorola_intel);
					data_ptr += 4;
					break;
				case TAG_FMT_SSHORT:
					php_ifd_set16u(data_ptr, info_value->i, motorola_intel);
					data_ptr += 2;
					break;
				case TAG_FMT_SLONG:
					php_ifd_set32u(data_ptr, info_value->i, motorola_intel);
					data_ptr += 4;
					break;
				case TAG_FMT_URATIONAL:
					php_ifd_set32u(data_ptr,   info_value->sr.num, motorola_intel);
					php_ifd_set32u(data_ptr+4, info_value->sr.den, motorola_intel);
					data_ptr += 8;
					break;
				case TAG_FMT_SRATIONAL:
					php_ifd_set32u(data_ptr,   info_value->ur.num, motorola_intel);
					php_ifd_set32u(data_ptr+4, info_value->ur.den, motorola_intel);
					data_ptr += 8;
					break;
				case TAG_FMT_SINGLE:
					memmove(data_ptr, &info_value->f, 4);
					data_ptr += 4;
					break;
				case TAG_FMT_DOUBLE:
					memmove(data_ptr, &info_value->d, 8);
					data_ptr += 8;
					break;
			}
		}
	}
	return value_ptr;
}


net-gro: fix use-after-free read in napi_gro_frags()

If a network driver provides to napi_gro_frags() an
skb with a page fragment of exactly 14 bytes, the call
to gro_pull_from_frag0() will 'consume' the fragment
by calling skb_frag_unref(skb, 0), and the page might
be freed and reused.

Reading eth->h_proto at the end of napi_frags_skb() might
read mangled data, or crash under specific debugging features.

BUG: KASAN: use-after-free in napi_frags_skb net/core/dev.c:5833 [inline]
BUG: KASAN: use-after-free in napi_gro_frags+0xc6f/0xd10 net/core/dev.c:5841
Read of size 2 at addr ffff88809366840c by task syz-executor599/8957

CPU: 1 PID: 8957 Comm: syz-executor599 Not tainted 5.2.0-rc1+ #32
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
 __dump_stack lib/dump_stack.c:77 [inline]
 dump_stack+0x172/0x1f0 lib/dump_stack.c:113
 print_address_description.cold+0x7c/0x20d mm/kasan/report.c:188
 __kasan_report.cold+0x1b/0x40 mm/kasan/report.c:317
 kasan_report+0x12/0x20 mm/kasan/common.c:614
 __asan_report_load_n_noabort+0xf/0x20 mm/kasan/generic_report.c:142
 napi_frags_skb net/core/dev.c:5833 [inline]
 napi_gro_frags+0xc6f/0xd10 net/core/dev.c:5841
 tun_get_user+0x2f3c/0x3ff0 drivers/net/tun.c:1991
 tun_chr_write_iter+0xbd/0x156 drivers/net/tun.c:2037
 call_write_iter include/linux/fs.h:1872 [inline]
 do_iter_readv_writev+0x5f8/0x8f0 fs/read_write.c:693
 do_iter_write fs/read_write.c:970 [inline]
 do_iter_write+0x184/0x610 fs/read_write.c:951
 vfs_writev+0x1b3/0x2f0 fs/read_write.c:1015
 do_writev+0x15b/0x330 fs/read_write.c:1058

Fixes: a50e233c50db ("net-gro: restore frag0 optimization")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

 */
static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
{
	struct sk_buff *skb = napi->skb;
	const struct ethhdr *eth;
	unsigned int hlen = sizeof(*eth);

	napi->skb = NULL;

	skb_reset_mac_header(skb);
	skb_gro_reset_offset(skb);

	eth = skb_gro_header_fast(skb, 0);
	if (unlikely(skb_gro_header_hard(skb, hlen))) {
		eth = skb_gro_header_slow(skb, hlen, 0);
		if (unlikely(!eth)) {
			net_warn_ratelimited("%s: dropping impossible skb from %s\n",
					     __func__, napi->dev->name);
			napi_reuse_skb(napi, skb);
			return NULL;
		}
	} else {
		gro_pull_from_frag0(skb, hlen);
		NAPI_GRO_CB(skb)->frag0 += hlen;
		NAPI_GRO_CB(skb)->frag0_len -= hlen;
	}
	__skb_pull(skb, hlen);

	/*
	 * This works because the only protocols we care about don't require
	 * special handling.
	 * We'll fix it up properly in napi_frags_finish()
	 */
	skb->protocol = eth->h_proto;

	return skb;

A flaw was found in the Linux kernel's implementation of GRO in versions before 5.2. This flaw allows an attacker with local access to crash the system.


 */
static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
{
	struct sk_buff *skb = napi->skb;
	const struct ethhdr *eth;
	unsigned int hlen = sizeof(*eth);

	napi->skb = NULL;

	skb_reset_mac_header(skb);
	skb_gro_reset_offset(skb);

	if (unlikely(skb_gro_header_hard(skb, hlen))) {
		eth = skb_gro_header_slow(skb, hlen, 0);
		if (unlikely(!eth)) {
			net_warn_ratelimited("%s: dropping impossible skb from %s\n",
					     __func__, napi->dev->name);
			napi_reuse_skb(napi, skb);
			return NULL;
		}
	} else {
		eth = (const struct ethhdr *)skb->data;
		gro_pull_from_frag0(skb, hlen);
		NAPI_GRO_CB(skb)->frag0 += hlen;
		NAPI_GRO_CB(skb)->frag0_len -= hlen;
	}
	__skb_pull(skb, hlen);

	/*
	 * This works because the only protocols we care about don't require
	 * special handling.
	 * We'll fix it up properly in napi_frags_finish()
	 */
	skb->protocol = eth->h_proto;

	return skb;


Fixed GHSL-2020-100: oob read in ntlm_read_ChallengeMessage

* Added length checks for data read from stream
* Unified function resource cleanup

(cherry picked from commit 8357dedbbf7213b7d0d18f1026145b9a5b92235a)

SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
{
	wStream* s;
	int length;
	PBYTE StartOffset;
	PBYTE PayloadOffset;
	NTLM_AV_PAIR* AvTimestamp;
	NTLM_CHALLENGE_MESSAGE* message;
	ntlm_generate_client_challenge(context);
	message = &context->CHALLENGE_MESSAGE;
	ZeroMemory(message, sizeof(NTLM_CHALLENGE_MESSAGE));
	s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer);

	if (!s)
		return SEC_E_INTERNAL_ERROR;

	StartOffset = Stream_Pointer(s);

	if (ntlm_read_message_header(s, (NTLM_MESSAGE_HEADER*)message) < 0)
	{
		Stream_Free(s, FALSE);
		return SEC_E_INVALID_TOKEN;
	}

	if (message->MessageType != MESSAGE_TYPE_CHALLENGE)
	{
		Stream_Free(s, FALSE);
		return SEC_E_INVALID_TOKEN;
	}

	if (ntlm_read_message_fields(s, &(message->TargetName)) < 0) /* TargetNameFields (8 bytes) */
	{
		Stream_Free(s, FALSE);
		return SEC_E_INVALID_TOKEN;
	}

	if (Stream_GetRemainingLength(s) < 4)
	{
		Stream_Free(s, FALSE);
		return SEC_E_INVALID_TOKEN;
	}

	Stream_Read_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */
	context->NegotiateFlags = message->NegotiateFlags;

	if (Stream_GetRemainingLength(s) < 8)
	{
		Stream_Free(s, FALSE);
		return SEC_E_INVALID_TOKEN;
	}

	Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
	CopyMemory(context->ServerChallenge, message->ServerChallenge, 8);

	if (Stream_GetRemainingLength(s) < 8)
	{
		Stream_Free(s, FALSE);
		return SEC_E_INVALID_TOKEN;
	}

	Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */

	if (ntlm_read_message_fields(s, &(message->TargetInfo)) < 0) /* TargetInfoFields (8 bytes) */
	{
		Stream_Free(s, FALSE);
		return SEC_E_INVALID_TOKEN;
	}

	if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
	{
		if (ntlm_read_version_info(s, &(message->Version)) < 0) /* Version (8 bytes) */
		{
			Stream_Free(s, FALSE);
			return SEC_E_INVALID_TOKEN;
		}
	}

	/* Payload (variable) */
	PayloadOffset = Stream_Pointer(s);

	if (message->TargetName.Len > 0)
	{
		if (ntlm_read_message_fields_buffer(s, &(message->TargetName)) < 0)
		{
			Stream_Free(s, FALSE);
			return SEC_E_INTERNAL_ERROR;
		}
	}

	if (message->TargetInfo.Len > 0)
	{
		size_t cbAvTimestamp;

		if (ntlm_read_message_fields_buffer(s, &(message->TargetInfo)) < 0)
		{
			Stream_Free(s, FALSE);
			return SEC_E_INTERNAL_ERROR;
		}

		context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer;
		context->ChallengeTargetInfo.cbBuffer = message->TargetInfo.Len;
		AvTimestamp = ntlm_av_pair_get((NTLM_AV_PAIR*)message->TargetInfo.Buffer,
		                               message->TargetInfo.Len, MsvAvTimestamp, &cbAvTimestamp);

		if (AvTimestamp)
		{
			PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp);

			if (!ptr)
				return SEC_E_INTERNAL_ERROR;

			if (context->NTLMv2)
				context->UseMIC = TRUE;

			CopyMemory(context->ChallengeTimestamp, ptr, 8);
		}
	}

	length = (PayloadOffset - StartOffset) + message->TargetName.Len + message->TargetInfo.Len;

	if (!sspi_SecBufferAlloc(&context->ChallengeMessage, length))
	{
		Stream_Free(s, FALSE);
		return SEC_E_INTERNAL_ERROR;
	}

	CopyMemory(context->ChallengeMessage.pvBuffer, StartOffset, length);
#ifdef WITH_DEBUG_NTLM
	WLog_DBG(TAG, "CHALLENGE_MESSAGE (length = %d)", length);
	winpr_HexDump(TAG, WLOG_DEBUG, context->ChallengeMessage.pvBuffer,
	              context->ChallengeMessage.cbBuffer);
	ntlm_print_negotiate_flags(context->NegotiateFlags);

	if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
		ntlm_print_version_info(&(message->Version));

	ntlm_print_message_fields(&(message->TargetName), "TargetName");
	ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo");

	if (context->ChallengeTargetInfo.cbBuffer > 0)
	{
		WLog_DBG(TAG, "ChallengeTargetInfo (%" PRIu32 "):", context->ChallengeTargetInfo.cbBuffer);
		ntlm_print_av_pair_list(context->ChallengeTargetInfo.pvBuffer,
		                        context->ChallengeTargetInfo.cbBuffer);
	}

#endif
	/* AV_PAIRs */

	if (context->NTLMv2)
	{
		if (ntlm_construct_authenticate_target_info(context) < 0)
		{
			Stream_Free(s, FALSE);
			return SEC_E_INTERNAL_ERROR;
		}

		sspi_SecBufferFree(&context->ChallengeTargetInfo);
		context->ChallengeTargetInfo.pvBuffer = context->AuthenticateTargetInfo.pvBuffer;
		context->ChallengeTargetInfo.cbBuffer = context->AuthenticateTargetInfo.cbBuffer;
	}

	ntlm_generate_timestamp(context); /* Timestamp */

	if (ntlm_compute_lm_v2_response(context) < 0) /* LmChallengeResponse */
	{
		Stream_Free(s, FALSE);
		return SEC_E_INTERNAL_ERROR;
	}

	if (ntlm_compute_ntlm_v2_response(context) < 0) /* NtChallengeResponse */
	{
		Stream_Free(s, FALSE);
		return SEC_E_INTERNAL_ERROR;
	}

	ntlm_generate_key_exchange_key(context);     /* KeyExchangeKey */
	ntlm_generate_random_session_key(context);   /* RandomSessionKey */
	ntlm_generate_exported_session_key(context); /* ExportedSessionKey */
	ntlm_encrypt_random_session_key(context);    /* EncryptedRandomSessionKey */
	/* Generate signing keys */
	ntlm_generate_client_signing_key(context);
	ntlm_generate_server_signing_key(context);
	/* Generate sealing keys */
	ntlm_generate_client_sealing_key(context);
	ntlm_generate_server_sealing_key(context);
	/* Initialize RC4 seal state using client sealing key */
	ntlm_init_rc4_seal_states(context);
#ifdef WITH_DEBUG_NTLM
	WLog_DBG(TAG, "ClientChallenge");
	winpr_HexDump(TAG, WLOG_DEBUG, context->ClientChallenge, 8);
	WLog_DBG(TAG, "ServerChallenge");
	winpr_HexDump(TAG, WLOG_DEBUG, context->ServerChallenge, 8);
	WLog_DBG(TAG, "SessionBaseKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->SessionBaseKey, 16);
	WLog_DBG(TAG, "KeyExchangeKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->KeyExchangeKey, 16);
	WLog_DBG(TAG, "ExportedSessionKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->ExportedSessionKey, 16);
	WLog_DBG(TAG, "RandomSessionKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->RandomSessionKey, 16);
	WLog_DBG(TAG, "ClientSigningKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSigningKey, 16);
	WLog_DBG(TAG, "ClientSealingKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSealingKey, 16);
	WLog_DBG(TAG, "ServerSigningKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSigningKey, 16);
	WLog_DBG(TAG, "ServerSealingKey");
	winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSealingKey, 16);
	WLog_DBG(TAG, "Timestamp");
	winpr_HexDump(TAG, WLOG_DEBUG, context->Timestamp, 8);
#endif
	context->state = NTLM_STATE_AUTHENTICATE;
	ntlm_free_message_fields_buffer(&(message->TargetName));
	Stream_Free(s, FALSE);
	return SEC_I_CONTINUE_NEEDED;
}

An issue was discovered in FreeRDP before 2.1.1. An out-of-bounds (OOB) read vulnerability has been detected in ntlm_read_ChallengeMessage in winpr/libwinpr/sspi/NTLM/ntlm_message.c.