1#!/bin/ksh -p
2#
3# CDDL HEADER START
4#
5# This file and its contents are supplied under the terms of the
6# Common Development and Distribution License ("CDDL"), version 1.0.
7# You may only use this file in accordance with the terms of version
8# 1.0 of the CDDL.
9#
10# A full copy of the text of the CDDL should have accompanied this
11# source.  A copy of the CDDL is also available via the Internet at
12# http://www.illumos.org/license/CDDL.
13#
14# CDDL HEADER END
15#
16
17#
18# Copyright (c) 2018 by Datto Inc. All rights reserved.
19# Copyright 2020 Joyent, Inc.
20#
21
22. $STF_SUITE/tests/functional/rsend/rsend.kshlib
23
24#
25# DESCRIPTION:
26# Verify that zfs properly handles encryption properties when receiving
27# send streams.
28#
29# STRATEGY:
30# 1. Create a few unencrypted and encrypted test datasets with some data
31# 2. Take snapshots of these datasets in preparation for sending
32# 3. Verify that 'zfs recv -o keylocation=prompt' fails
33# 4. Verify that 'zfs recv -x <encryption prop>' fails on a raw send stream
34# 5. Verify that encryption properties cannot be changed on incrementals
35# 6. Verify that a simple send can be received as an encryption root
36# 7. Verify that an unencrypted props send can be received as an
37#    encryption root
38# 8. Verify that an unencrypted recursive send can be received as an
39#    encryption root
40# 9. Verify that an unencrypted props send can be received as an
41#    encryption child
42# 10. Verify that an unencrypted recursive send can be received as an
43#     encryption child
44#
45
46verify_runnable "both"
47
48function cleanup
49{
50	destroy_dataset $TESTPOOL/recv "-r"
51	destroy_dataset $TESTPOOL/crypt "-r"
52	destroy_dataset $TESTPOOL/ds "-r"
53	[[ -f $sendfile ]] && log_must rm $sendfile
54	[[ -f $keyfile ]] && log_must rm $keyfile
55}
56log_onexit cleanup
57
58log_assert "'zfs recv' must properly handle encryption properties"
59
60typeset keyfile=/$TESTPOOL/pkey
61typeset sendfile=/$TESTPOOL/sendfile
62typeset snap=$TESTPOOL/ds@snap1
63typeset snap2=$TESTPOOL/ds@snap2
64typeset esnap=$TESTPOOL/crypt@snap1
65typeset esnap2=$TESTPOOL/crypt@snap2
66
67log_must eval "echo 'password' > $keyfile"
68
69log_must zfs create $TESTPOOL/ds
70log_must zfs create $TESTPOOL/ds/ds1
71
72log_must zfs create -o encryption=on -o keyformat=passphrase \
73	-o keylocation=file://$keyfile $TESTPOOL/crypt
74log_must zfs create $TESTPOOL/crypt/ds1
75log_must zfs create -o keyformat=passphrase -o keylocation=file://$keyfile \
76	$TESTPOOL/crypt/ds2
77
78log_must mkfile 1M /$TESTPOOL/ds/$TESTFILE0
79log_must cp /$TESTPOOL/ds/$TESTFILE0 /$TESTPOOL/crypt/$TESTFILE0
80typeset cksum=$(digest -a md5 /$TESTPOOL/ds/$TESTFILE0)
81
82log_must zfs snap -r $snap
83log_must zfs snap -r $snap2
84log_must zfs snap -r $esnap
85log_must zfs snap -r $esnap2
86
87# Embedded data is incompatible with encrypted datasets, so we cannot
88# allow embedded streams to be received.
89log_note "Must not be able to receive an embedded stream as encrypted"
90log_mustnot eval "zfs send -e $TESTPOOL/crypt/ds1 | zfs recv $TESTPOOL/recv"
91
92# We currently don't have an elegant and secure way to pass the passphrase
93# in via prompt because the send stream itself is using stdin.
94log_note "Must not be able to use 'keylocation=prompt' on receive"
95log_must eval "zfs send $snap > $sendfile"
96log_mustnot eval "zfs recv -o encryption=on -o keyformat=passphrase" \
97	"$TESTPOOL/recv < $sendfile"
98log_mustnot eval "zfs recv -o encryption=on -o keyformat=passphrase" \
99	"-o keylocation=prompt $TESTPOOL/recv < $sendfile"
100
101# Raw sends do not have access to the decrypted data so we cannot override
102# the encryption settings without losing the data.
103log_note "Must not be able to disable encryption properties on raw send"
104log_must eval "zfs send -w $esnap > $sendfile"
105log_mustnot eval "zfs recv -x encryption $TESTPOOL/recv < $sendfile"
106log_mustnot eval "zfs recv -x keyformat $TESTPOOL/recv < $sendfile"
107log_mustnot eval "zfs recv -x pbkdf2iters $TESTPOOL/recv < $sendfile"
108
109# Encryption properties are set upon creating the dataset. Changing them
110# afterwards requires using 'zfs change-key' or an update from a raw send.
111log_note "Must not be able to change encryption properties on incrementals"
112log_must eval "zfs send $esnap | zfs recv -o encryption=on" \
113	"-o keyformat=passphrase -o keylocation=file://$keyfile $TESTPOOL/recv"
114log_mustnot eval "zfs send -i $esnap $esnap2 |" \
115	"zfs recv -o encryption=aes-128-ccm $TESTPOOL/recv"
116log_mustnot eval "zfs send -i $esnap $esnap2 |" \
117	"zfs recv -o keyformat=hex $TESTPOOL/recv"
118log_mustnot eval "zfs send -i $esnap $esnap2 |" \
119	"zfs recv -o pbkdf2iters=100k $TESTPOOL/recv"
120log_must zfs destroy -r $TESTPOOL/recv
121
122# Test that we can receive a simple stream as an encryption root.
123log_note "Must be able to receive stream as encryption root"
124ds=$TESTPOOL/recv
125log_must eval "zfs send $snap > $sendfile"
126log_must eval "zfs recv -o encryption=on -o keyformat=passphrase" \
127	"-o keylocation=file://$keyfile $ds < $sendfile"
128log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
129log_must test "$(get_prop 'encryptionroot' $ds)" == "$ds"
130log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
131log_must test "$(get_prop 'keylocation' $ds)" == "file://$keyfile"
132log_must test "$(get_prop 'mounted' $ds)" == "yes"
133recv_cksum=$(digest -a md5 /$ds/$TESTFILE0)
134log_must test "$recv_cksum" == "$cksum"
135log_must zfs destroy -r $ds
136
137# Test that we can override encryption properties on a properties stream
138# of an unencrypted dataset, turning it into an encryption root.
139log_note "Must be able to receive stream with props as encryption root"
140ds=$TESTPOOL/recv
141log_must eval "zfs send -p $snap > $sendfile"
142log_must eval "zfs recv -o encryption=on -o keyformat=passphrase" \
143	"-o keylocation=file://$keyfile $ds < $sendfile"
144log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
145log_must test "$(get_prop 'encryptionroot' $ds)" == "$ds"
146log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
147log_must test "$(get_prop 'keylocation' $ds)" == "file://$keyfile"
148log_must test "$(get_prop 'mounted' $ds)" == "yes"
149recv_cksum=$(digest -a md5 /$ds/$TESTFILE0)
150log_must test "$recv_cksum" == "$cksum"
151log_must zfs destroy -r $ds
152
153# Test that we can override encryption properties on a recursive stream
154# of an unencrypted dataset, turning it into an encryption root. The root
155# dataset of the stream should become an encryption root with all children
156# inheriting from it.
157log_note "Must be able to receive recursive stream as encryption root"
158ds=$TESTPOOL/recv
159log_must eval "zfs send -R $snap > $sendfile"
160log_must eval "zfs recv -o encryption=on -o keyformat=passphrase" \
161	"-o keylocation=file://$keyfile $ds < $sendfile"
162log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
163log_must test "$(get_prop 'encryptionroot' $ds)" == "$ds"
164log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
165log_must test "$(get_prop 'keylocation' $ds)" == "file://$keyfile"
166log_must test "$(get_prop 'mounted' $ds)" == "yes"
167recv_cksum=$(digest -a md5 /$ds/$TESTFILE0)
168log_must test "$recv_cksum" == "$cksum"
169log_must zfs destroy -r $ds
170
171# Test that we can override an unencrypted properties stream's encryption
172# settings, receiving it as an encrypted child.
173log_note "Must be able to receive stream with props to encrypted child"
174ds=$TESTPOOL/crypt/recv
175log_must eval "zfs send -p $snap > $sendfile"
176log_must eval "zfs recv -x encryption $ds < $sendfile"
177log_must test "$(get_prop 'encryptionroot' $ds)" == "$TESTPOOL/crypt"
178log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
179log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
180log_must test "$(get_prop 'mounted' $ds)" == "yes"
181recv_cksum=$(digest -a md5 /$ds/$TESTFILE0)
182log_must test "$recv_cksum" == "$cksum"
183log_must zfs destroy -r $ds
184
185# Test that we can override an unencrypted, incremental, properties stream's
186# encryption settings, receiving it as an unencrypted child.
187log_note "Must be able to receive incremental stream with props to encrypted"
188ds=$TESTPOOL/crypt/recv
189log_must eval "zfs send -p $snap > $sendfile"
190log_must eval "zfs recv -x encryption $ds < $sendfile"
191log_must test "$(get_prop 'encryptionroot' $ds)" == "$TESTPOOL/crypt"
192log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
193log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
194log_must test "$(get_prop 'mounted' $ds)" == "yes"
195recv_cksum=$(digest -a md5 /$ds/$TESTFILE0)
196log_must test "$recv_cksum" == "$cksum"
197log_must zfs destroy -r $ds
198
199# Test that we can override an unencrypted recursive stream's encryption
200# settings, receiving all datasets as encrypted children.
201log_note "Must be able to receive recursive stream to encrypted child"
202ds=$TESTPOOL/crypt/recv
203log_must eval "zfs send -R $snap2 > $sendfile"
204log_must eval "zfs recv -x encryption $ds < $sendfile"
205log_must test "$(get_prop 'encryptionroot' $ds)" == "$TESTPOOL/crypt"
206log_must test "$(get_prop 'encryption' $ds)" == "aes-256-ccm"
207log_must test "$(get_prop 'keyformat' $ds)" == "passphrase"
208log_must test "$(get_prop 'mounted' $ds)" == "yes"
209recv_cksum=$(digest -a md5 /$ds/$TESTFILE0)
210log_must test "$recv_cksum" == "$cksum"
211log_must zfs destroy -r $ds
212
213# Check that we haven't printed the key to the zpool history log
214log_mustnot eval "zpool history -i | grep -q 'wkeydata'"
215
216log_pass "'zfs recv' properly handles encryption properties"
217