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